Agenda

Announcements

  • Final Project update
  • MDSR Ch 15 programming notebook assigned
  • Beckman’s office hour cancelled on Thurs 4/4 (see syllabus for other options)
  • MDSR Ch 15 Exercises due 4/14 before midnight
  • Lots of errata/tips this chapter

MDSR Ch 15 Errata / Tips

  • Some sections don’t require programming, but please still include the headers for navigation purposes
  • p. 361: Note–data("DataSciencePapers") should appear BOTH in front matter (per style guide) AND as a code chunk in sequence with the other MDSR code so your section 15.2 results in the programming notebook will match the MDSR book rather than using new data queried from arXiv also loaded on p. 361 under the object name DataSciencePapers
  • p. 365: your word cloud won’t be identical and may even exclude “data” if it doesn’t fit on the page
  • p. 368: Wikipedia changed the tables all around. Use Table[[4]] when scraping; Title is now Song; the results won’t exactly match but you’ll be able to work it out.
  • p. 369: change 25 to 15 (the result is quite a different message!)
  • p. 370: you don’t need a Twitter account, the provided credentials work… here they are:
    • consumer_key = “u2UthjbK6YHyQSp4sPk6yjsuV”
    • consumer_secret = “sC4mjd2WME5nH1FoWeSTuSy7JCP5DHjNtTYU1X6BwQ1vPZ0j3v”
    • access_token = “1365606414-7vPfPxStYNq6kWEATQlT8HZBd4G83BBcX4VoS9T”
    • access_secret = “0hJq9KYC3eBRuZzJqSacmtJ4PNJ7tNLkGrQrVl00JHirs”
  • p. 372: you’ll need to load the RSQLite library
  • p. 373: you can skip the geocode(...) function from the ggmap package and just assign lon and lat directly. You can even look up the coordinates for State College and substitute that if you like. Oddly enough, you might have to give a credit card number to Google if you want to use geocode() so feel free to modify your code to avoid that step.

Road map

Project Gutenberg

Macbeth Summary

Our “muse” for this first portion is the famous play MacBeth by William Shakespeare. If you aren’t familiar with the play, here is a quick summary:

Text as data

Macbeth text data intake

macbeth_url <- "http://www.gutenberg.org/cache/epub/1129/pg1129.txt"
Macbeth_raw <- RCurl::getURL(macbeth_url)
# Macbeth_raw

Text as data

Back to Macbeth

macbeth_tmp <- strsplit(Macbeth_raw, "\r\n")
str(macbeth_tmp)
List of 1
 $ : chr [1:3193] "This Etext file is presented by Project Gutenberg, in" "cooperation with World Library, Inc., from their Library of the" "Future and Shakespeare CDROMS.  Project Gutenberg often releases" "Etexts that are NOT placed in the Public Domain!!" ...
macbeth <- strsplit(Macbeth_raw, "\r\n")[[1]]
length(macbeth)
[1] 3193
head(macbeth)
[1] "This Etext file is presented by Project Gutenberg, in"           
[2] "cooperation with World Library, Inc., from their Library of the" 
[3] "Future and Shakespeare CDROMS.  Project Gutenberg often releases"
[4] "Etexts that are NOT placed in the Public Domain!!"               
[5] ""                                                                
[6] "*This Etext has certain copyright implications you should read!*"

The set up…

Manual text analysis

macbeth[295:310]
 [1] ""                                                          
 [2] "SCENE II."                                                 
 [3] "A camp near Forres. Alarum within."                        
 [4] ""                                                          
 [5] "Enter Duncan, Malcolm, Donalbain, Lennox, with Attendants,"
 [6] "meeting a bleeding Sergeant."                              
 [7] ""                                                          
 [8] "  DUNCAN. What bloody man is that? He can report,"         
 [9] "    As seemeth by his plight, of the revolt"               
[10] "    The newest state."                                     
[11] "  MALCOLM. This is the sergeant"                           
[12] "    Who like a good and hardy soldier fought"              
[13] "    'Gainst my captivity. Hail, brave friend!"             
[14] "    Say to the King the knowledge of the broil"            
[15] "    As thou didst leave it."                               
[16] "  SERGEANT. Doubtful it stood,"                            

Manual text analysis

Regular Expressions (RegEx)

grep( ) & grepl( )

macbeth_lines <- grep("MACBETH", macbeth)
# macbeth_lines <- grep("MACBETH", macbeth, value = TRUE)
# macbeth_lines <- grepl("MACBETH", macbeth)
length(macbeth_lines)
[1] 208
head(macbeth_lines)
[1] 218 228 230 433 443 466
identical(c(1:3), c(1L, 2L, 3L))  # are these the same?
identical(c(1:3), c(1, 2, 3))     # `identical` is PICKY

# are these the same?
identical(macbeth[grep("MACBETH", macbeth)],   
          macbeth[grepl("MACBETH", macbeth)])

Refining our RegEx

macbeth_lines <- grep("  MACBETH.", macbeth, value = TRUE)
length(macbeth_lines)
[1] 147
head(macbeth_lines)
[1] "  MACBETH, Thane of Glamis and Cawdor, a general in the King's"
[2] "  MACBETH. So foul and fair a day I have not seen."            
[3] "  MACBETH. Speak, if you can. What are you?"                   
[4] "  MACBETH. Stay, you imperfect speakers, tell me more."        
[5] "  MACBETH. Into the air, and what seem'd corporal melted"      
[6] "  MACBETH. Your children shall be kings."                      

Problem with period (.)

# anything with "MAC" and then another character
grep("MAC.", macbeth, value = TRUE) %>%
  head()
[1] "MACHINE READABLE COPIES MAY BE DISTRIBUTED SO LONG AS SUCH COPIES"
[2] "MACHINE READABLE COPIES OF THIS ETEXT, SO LONG AS SUCH COPIES"    
[3] "WITH PERMISSION.  ELECTRONIC AND MACHINE READABLE COPIES MAY BE"  
[4] "THE TRAGEDY OF MACBETH"                                           
[5] "  MACBETH, Thane of Glamis and Cawdor, a general in the King's"   
[6] "  LADY MACBETH, his wife"                                         
# MACBETH.
grep("MACBETH\\.", macbeth, value = TRUE) %>% head(20)
 [1] "  MACBETH. So foul and fair a day I have not seen."              
 [2] "  MACBETH. Speak, if you can. What are you?"                     
 [3] "  MACBETH. Stay, you imperfect speakers, tell me more."          
 [4] "  MACBETH. Into the air, and what seem'd corporal melted"        
 [5] "  MACBETH. Your children shall be kings."                        
 [6] "  MACBETH. And Thane of Cawdor too. Went it not so?"             
 [7] "  MACBETH. The Thane of Cawdor lives. Why do you dress me"       
 [8] "  MACBETH. [Aside.] Glamis, and Thane of Cawdor!"                
 [9] "  MACBETH. [Aside.] Two truths are told,"                        
[10] "  MACBETH. [Aside.] If chance will have me King, why, chance may"
[11] "  MACBETH. [Aside.] Come what come may,"                         
[12] "  MACBETH. Give me your favor; my dull brain was wrought"        
[13] "  MACBETH. Till then, enough. Come, friends.             Exeunt."
[14] "  MACBETH. The service and the loyalty lowe,"                    
[15] "  MACBETH. The rest is labor, which is not used for you."        
[16] "  MACBETH. [Aside.] The Prince of Cumberland! That is a step"    
[17] "  LADY MACBETH. \"They met me in the day of success, and I have" 
[18] "  LADY MACBETH. Thou'rt mad to say it!"                          
[19] "  LADY MACBETH. Give him tending;"                               
[20] "  MACBETH. My dearest love,"                                     

Simple RegEx Tools

# alternation with `|`
grep("MAC[B|D]", macbeth, value = TRUE) %>% head()
[1] "THE TRAGEDY OF MACBETH"                                        
[2] "  MACBETH, Thane of Glamis and Cawdor, a general in the King's"
[3] "  LADY MACBETH, his wife"                                      
[4] "  MACDUFF, Thane of Fife, a nobleman of Scotland"              
[5] "  LADY MACDUFF, his wife"                                      
[6] "  MACBETH. So foul and fair a day I have not seen."            
# "MAC" followed by any capital letter from "C" through "Z"
grep("MAC[C-Z]", macbeth, value = TRUE) %>% head(10)
 [1] "MACHINE READABLE COPIES MAY BE DISTRIBUTED SO LONG AS SUCH COPIES"
 [2] "MACHINE READABLE COPIES OF THIS ETEXT, SO LONG AS SUCH COPIES"    
 [3] "WITH PERMISSION.  ELECTRONIC AND MACHINE READABLE COPIES MAY BE"  
 [4] "  MACDUFF, Thane of Fife, a nobleman of Scotland"                 
 [5] "  LADY MACDUFF, his wife"                                         
 [6] "WITH PERMISSION.  ELECTRONIC AND MACHINE READABLE COPIES MAY BE"  
 [7] "WITH PERMISSION.  ELECTRONIC AND MACHINE READABLE COPIES MAY BE"  
 [8] "  MACDUFF. Was it so late, friend, ere you went to bed,"          
 [9] "  MACDUFF. What three things does drink especially provoke?"      
[10] "  MACDUFF. I believe drink gave thee the lie last night."         
# search for beginning of each act in the play (`^` goea at beginning)
grep("ACT", macbeth, value = TRUE) %>% head(10)
[1] "BREACH OF WARRANTY OR CONTRACT, INCLUDING BUT NOT LIMITED TO"
[2] "ACT I. SCENE I."                                             
[3] "ACT II. SCENE I."                                            
[4] "ACT III. SCENE I."                                           
[5] "ACT IV. SCENE I."                                            
[6] "ACT V. SCENE I."                                             
grep("^ACT", macbeth, value = TRUE) %>% head(10)
[1] "ACT I. SCENE I."   "ACT II. SCENE I."  "ACT III. SCENE I." "ACT IV. SCENE I."  "ACT V. SCENE I."  
# strings strictly ending in "MACBETH" (`$` goes at end)
grep("MACBETH$", macbeth, value = TRUE) %>% head(10)
[1] "THE TRAGEDY OF MACBETH"
# repetitions
grep("^ ?MAC", macbeth, value = TRUE) %>% head()  # zero or one leading spaces
[1] "MACHINE READABLE COPIES MAY BE DISTRIBUTED SO LONG AS SUCH COPIES"
[2] "MACHINE READABLE COPIES OF THIS ETEXT, SO LONG AS SUCH COPIES"    
grep("^ *MAC", macbeth, value = TRUE) %>% head()  # zero or more leading spaces
[1] "MACHINE READABLE COPIES MAY BE DISTRIBUTED SO LONG AS SUCH COPIES"
[2] "MACHINE READABLE COPIES OF THIS ETEXT, SO LONG AS SUCH COPIES"    
[3] "  MACBETH, Thane of Glamis and Cawdor, a general in the King's"   
[4] "  MACDUFF, Thane of Fife, a nobleman of Scotland"                 
[5] "  MACBETH. So foul and fair a day I have not seen."               
[6] "  MACBETH. Speak, if you can. What are you?"                      
grep("^ +MAC", macbeth, value = TRUE) %>% head()  # one or more leading spaces
[1] "  MACBETH, Thane of Glamis and Cawdor, a general in the King's"
[2] "  MACDUFF, Thane of Fife, a nobleman of Scotland"              
[3] "  MACBETH. So foul and fair a day I have not seen."            
[4] "  MACBETH. Speak, if you can. What are you?"                   
[5] "  MACBETH. Stay, you imperfect speakers, tell me more."        
[6] "  MACBETH. Into the air, and what seem'd corporal melted"      
grep("^ {2}MAC", macbeth, value = TRUE) %>% head()  # exaclty two leading spaces
[1] "  MACBETH, Thane of Glamis and Cawdor, a general in the King's"
[2] "  MACDUFF, Thane of Fife, a nobleman of Scotland"              
[3] "  MACBETH. So foul and fair a day I have not seen."            
[4] "  MACBETH. Speak, if you can. What are you?"                   
[5] "  MACBETH. Stay, you imperfect speakers, tell me more."        
[6] "  MACBETH. Into the air, and what seem'd corporal melted"      
grep("^ {3}MAC", macbeth, value = TRUE) %>% head()  # exactly three leading spaces
character(0)

Speaker frequency in Macbeth

Macbeth <- grepl("  MACBETH\\.", macbeth)
Macduff <- grepl("  MACDUFF\\.", macbeth)
LadyMacbeth <- grepl("  LADY MACBETH\\.", macbeth)
LadyMacduff <- grepl("  LADY MACDUFF\\.", macbeth)
Banquo <- grepl("  BANQUO\\.", macbeth)
Duncan <- grepl("  DUNCAN\\.", macbeth)
speaker_freq <- data.frame(Macbeth, Macduff, LadyMacbeth, LadyMacduff, Banquo, Duncan) %>%
  mutate(line = 1:length(macbeth)) %>%
  gather(key = "character", value = "speak", -line) %>%
  mutate(speak = as.numeric(speak)) %>%
  filter(line > 218 & line < 3172)
glimpse(speaker_freq)
Observations: 17,718
Variables: 3
$ line      <int> 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238,…
$ character <chr> "Macbeth", "Macbeth", "Macbeth", "Macbeth", "Macbeth", "Macbeth", "Macbeth", "Macbeth", "Macbeth", …
$ speak     <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
# find the acts that subdivide the play
acts_idx <- grep("^ACT ", macbeth)
acts_labels <- str_extract(macbeth[acts_idx], "^ACT [I|V]+")  # [I|V]+ I, II, III, IV, V
acts <- data.frame(line = acts_idx, labels = acts_labels)
# plot speaker frequencies
speaker_freq %>%
  ggplot(aes(x = line, y = speak)) + 
  geom_smooth(aes(color = character), method = "loess", se = 0) + 
  geom_vline(xintercept = acts_idx, color = "darkgray", lty = 3) + 
  geom_text(data = acts, aes(y = 0.085, label = labels), 
            hjust = "left", color = "darkgray") + 
  ylim(c(0, NA)) + 
  xlab("Line Number") + 
  ylab("Proportion of Speeches")

Further Analysis

Indentifying lines & speakers

lines <- strsplit(Macbeth_raw, "\r\n {2}[A-Z| |0-9]*\\. ")[[1]][-1]
speakers <- stringr::str_extract_all(Macbeth_raw, " {2}[A-Z| |0-9]*\\. ")[[1]]
AllMacbeth <- data.frame(speakers, lines)

Clean up the lines

head(AllMacbeth)

Clean up the lines

AllMacbeth <- 
  AllMacbeth %>%
  mutate(lines = gsub(pattern = "[\r|\n]*", replacement = "", x = lines))
head(AllMacbeth)

More clean up

<<THIS ELECTRONIC VERSION OF THE COMPLETE WORKS OF WILLIAM SHAKESPEARE IS COPYRIGHT 1990-1993 BY WORLD LIBRARY, INC., AND IS PROVIDED BY PROJECT GUTENBERG ETEXT OF CARNEGIE MELLON UNIVERSITY WITH PERMISSION. ELECTRONIC AND MACHINE READABLE COPIES MAY BE DISTRIBUTED SO LONG AS SUCH COPIES (1) ARE FOR YOUR OR OTHERS PERSONAL USE ONLY, AND (2) ARE NOT DISTRIBUTED OR USED COMMERCIALLY. PROHIBITED COMMERCIAL DISTRIBUTION INCLUDES BY ANY SERVICE THAT CHARGES FOR DOWNLOAD TIME OR FOR MEMBERSHIP.>>

Another gsub

junk <- grepl(pattern = "<<.*>>", x = AllMacbeth$lines)
# Before
AllMacbeth %>%
  filter(junk) %>%
  select(speakers, lines)
# Correction
AllMacbeth <- 
  AllMacbeth %>%
  mutate(lines = gsub(pattern = "<<.*>>", replacement = "", x = lines))
# After
AllMacbeth %>%
  filter(junk) %>%
  select(speakers, lines)

Time to take a look!

First attempt: Counting words

countWords <- function(line) {
  return(length(strsplit(x = line, split = "\\s+")))
}
AllMacbeth$nWords <- sapply(X = AllMacbeth$lines, FUN = countWords)
head(AllMacbeth)

Debugging our cleanup

strsplit(x = AllMacbeth$lines[1], split = "\\s+")
[[1]]
 [1] "When"       "shall"      "we"         "three"      "meet"       "again?"     "In"         "thunder,"  
 [9] "lightning," "or"         "in"         "rain?"     

Debugging our cleanup

countWords <- function(line) {
  return(length(strsplit(x = line, split = "\\s+")[[1]]))
}
AllMacbeth$nWords <- sapply(X = AllMacbeth$lines, FUN = countWords)
head(AllMacbeth)

Choosing your character

# Longest individual lines 
AllMacbeth %>%
  select(speakers, nWords, lines) %>%
  arrange(desc(nWords)) 

Character analysis

MacbethSummary <- 
  AllMacbeth %>% 
  group_by(speakers) %>%
  summarise(lines = n(), 
            totalWords = sum(nWords), 
            wordsPerLine = totalWords/lines)
# Most words per line
MacbethSummary %>%
  arrange(desc(wordsPerLine)) %>%
  head(10)
# Most total content
MacbethSummary %>%
  arrange(desc(totalWords)) %>%
  head(10)

Who do you choose?

MyLines <- 
  AllMacbeth %>% 
  filter(grepl("  LADY MACBETH\\.", speakers))
head(MyLines)

Text Mining Macbeth

Text Mining Macbeth

Text Analysis

require(readtext)
ModernMacbeth <- 
  readtext::readtext(file = "/Users/mattbeckman/Documents/GitHub/Teaching/STAT-380/2019 Spring/ClassNotes/15-mdsr/plain_Macbeth/*.txt", docvarsfrom = "filenames", dvsep = "_") %>%
  as.tibble()
head(ModernMacbeth)

Corpus

Modern Macbeth Corpus

Tidy Text Format

Tokenization step

require(tidytext)
ModernMacbeth %>%
  select(text, act = docvar1, scene = docvar2) %>%
  unnest_tokens(output = word, input = text)         # single word tokenization
  # unnest_tokens(output = word, input = text, token = "ngrams", n = 3)   # n-gram tokenization

Bag of words

ModernMacbeth_tidy <- 
  ModernMacbeth %>%
  select(text, act = docvar1, scene = docvar2) %>%
  unnest_tokens(output = word, input = text)         # single word tokenization

Token frequency

ModernMacbeth_tidy %>%
  count(word, sort = TRUE)

Stop words

# native text
ModernMacbeth_tidy %>%
  count(word, sort = TRUE)
# load stop word list (English)
data("stop_words")   # from `tidytext` package
head(stop_words)
tail(stop_words)

Removing stop words

# # `stop_words` has two columns: "word" and "lexicon
# stop_words <-
#   rbind(stop_words,           
#         c("macbeth", "custom"))
# 
# stop_words %>%
#   filter(word == "it's")
# 
# # we'll need to use RegEx to clean these up
# grep(pattern = "it.s", x = ModernMacbeth_tidy$word, value = TRUE)
# grep(pattern = "’", x = ModernMacbeth_tidy$word, value = TRUE) %>% head(30)
ModernMacbeth_tidy <- 
  ModernMacbeth_tidy %>%
  # mutate(word = gsub(pattern = , replacement = , x = word)) %>%
  filter(!(word %in% stop_words$word))
ModernMacbeth_tidy %>%
  count(word, sort = TRUE)

Tidy text pipes to ggplot2

ModernMacbeth_tidy %>%
  mutate(word = gsub(pattern = "’", replacement = "'", x = word)) %>%
  count(word, sort = TRUE) %>%
  filter(n > 20) %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(word, n)) +
  geom_col() +
  xlab(NULL) +
  coord_flip()

Word clouds

ModernMacbeth_tidy %>%
  filter(!(word %in% stop_words$word)) %>%
  count(word) %>%
  with(., wordcloud(word, n, max.words = 45))

word clouds: Modern vs original

AllMacbeth_tidy <- 
  AllMacbeth %>%
  select(speakers, lines) %>%
  unnest_tokens(output = word, input = lines)     # single word tokenization
head(AllMacbeth_tidy)
# helper function for making wordclouds
macbeth_wordcloud <- function(Corpus_tidy, maxWords = 45) {
  Corpus_tidy %>%
    filter(!(word %in% stop_words$word)) %>%
    count(word) %>%
    with(., wordcloud(word, n, max.words = maxWords))
}
macbeth_wordcloud(Corpus = AllMacbeth_tidy)

macbeth_wordcloud(Corpus = ModernMacbeth_tidy)

Act by Act word clouds

macbeth_wordcloud(filter(ModernMacbeth_tidy, act == "Act1"))

macbeth_wordcloud(filter(ModernMacbeth_tidy, act == "Act2"))

macbeth_wordcloud(filter(ModernMacbeth_tidy, act == "Act3"))

macbeth_wordcloud(filter(ModernMacbeth_tidy, act == "Act4"))

macbeth_wordcloud(filter(ModernMacbeth_tidy, act == "Act5"))

Recall

Sentiment

require(tidytext)
data("sentiments")
head(sentiments, 10)
tail(sentiments, 10)

Sentiment lexicons

get_sentiments("nrc") %>%
  group_by(sentiment) %>%
  summarise(N = n()) %>%
  arrange(desc(N))

Sentiment analysis of Macbeth

nrc_anticipation <- get_sentiments("nrc") %>%
  filter(sentiment == "anticipation")
ModernMacbeth_tidy %>%
  inner_join(nrc_anticipation) %>%
  count(word, sort = TRUE)
Joining, by = "word"
NA

Changes in Sentiment

ModernMacbeth_sentiment <- 
  ModernMacbeth_tidy %>%
  mutate(act_scene = paste(act, scene, sep = "_")) %>%
  inner_join(get_sentiments("afinn")) %>%
  group_by(act, scene) %>%
  summarise(sentiment = mean(score, na.rm = TRUE)) 
Joining, by = "word"
head(ModernMacbeth_sentiment)

Plot changes in sentiment by Act

ModernMacbeth_sentiment %>%
  rownames_to_column() %>%
  mutate(rowname = parse_number(rowname)) %>%
  ggplot(aes(x = rowname, y = sentiment, fill = act)) + 
  geom_bar(stat = "identity", show.legend = FALSE) + 
  ggtitle("Sentiment Analysis of each Act & Scene in Modern Macbeth")

NA

Common positive and negative words

bing_word_counts <- 
  ModernMacbeth_tidy %>%
  inner_join(get_sentiments("bing")) %>%
  count(word, sentiment, sort = TRUE) %>%
  ungroup()
Joining, by = "word"
bing_word_counts

Plotting word frequency by sentiment

bing_word_counts %>%
  group_by(sentiment) %>%
  top_n(10) %>%
  ungroup() %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(word, n, fill = sentiment)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~sentiment, scales = "free_y") +
  labs(y = "Contribution to sentiment",
       x = NULL) +
  coord_flip()
Selecting by n

Wordclouds with feeling

ModernMacbeth_tidy %>%
  anti_join(stop_words) %>%
  count(word) %>%
  with(wordcloud(word, n, max.words = 45))
require(reshape2)
ModernMacbeth_tidy %>%
  inner_join(get_sentiments("bing")) %>%
  count(word, sentiment, sort = TRUE) %>%
  acast(word ~ sentiment, value.var = "n", fill = 0) %>%
  comparison.cloud(colors = c("gray20", "gray80"), max.words = 45)
Joining, by = "word"

Positive scenes in Macbeth

bingpositive <- get_sentiments("bing") %>% 
  filter(sentiment == "positive")
wordcounts <- ModernMacbeth_tidy %>%
  group_by(act, scene) %>%
  summarize(words = n())
ModernMacbeth_tidy %>%
  semi_join(bingpositive) %>%
  group_by(act, scene) %>%
  summarize(positivewords = n()) %>%
  left_join(wordcounts, by = c("act", "scene")) %>%
  mutate(ratio = positivewords / words) %>%         # ratio of positive words
  filter(scene != 0) %>%
  top_n(1) %>%
  ungroup()
Joining, by = "word"
Selecting by ratio

Analysis of word & document frequency

Document term matrices

\[idf(t, D) = \text{ln}\frac{|D|}{|\{d \in D : t \in d\}|}\]

Term frequency in Acts of Macbeth

# tf within each act
act_words <- 
  ModernMacbeth %>%
  select(text, act = docvar1, scene = docvar2) %>%
  unnest_tokens(output = word, input = text) %>%
  mutate(word = gsub(pattern = "’", replacement = "'", x = word)) %>%
  count(act, word, sort = TRUE)
# total words in the act
total_words <- 
  act_words %>% 
  group_by(act) %>% 
  summarize(total = sum(n))
act_words <- left_join(act_words, total_words)
Joining, by = "act"
act_words

Term frequency in Macbeth

act_words %>% 
  ggplot(aes(n / total, fill = act)) +
  geom_histogram(show.legend = FALSE) +
  facet_wrap(~ act, ncol = 2, scales = "free_y") + 
  ggtitle("Term frequency in Acts of Macbeth")

Zipf’s law

freq_by_rank <- 
  act_words %>% 
  group_by(act) %>% 
  mutate(rank = row_number(),   # since already ordered by `n`
         `term frequency` = n / total)
freq_by_rank

Visualization of Zipf’s law

freq_by_rank %>% 
  ggplot(aes(rank, `term frequency`, color = act)) + 
  geom_line(size = 1.1, alpha = 0.8, show.legend = TRUE) + 
  scale_x_log10() +
  scale_y_log10() + 
  ggtitle("Zipf's law for acts of Macbeth")

Investigating term frequency in Macbeth

rank_subset <- freq_by_rank %>% 
  filter(rank < 200,
         rank > 10)
lm(log10(`term frequency`) ~ log10(rank), data = rank_subset)

Call:
lm(formula = log10(`term frequency`) ~ log10(rank), data = rank_subset)

Coefficients:
(Intercept)  log10(rank)  
     -0.946       -0.909  
freq_by_rank %>% 
  ggplot(aes(rank, `term frequency`, color = act)) + 
  geom_abline(intercept = -0.946, slope = -0.909, color = "gray50", linetype = 2) +
  geom_line(size = 1.1, alpha = 0.8, show.legend = TRUE) + 
  scale_x_log10() +
  scale_y_log10() + 
  ggtitle("Zipf's law for Modern Macbeth")

tf-idf

act_words <- 
  act_words %>%
  bind_tf_idf(word, act, n)
act_words

High tf-idf in Macbeth

act_words %>%
  select(-total) %>%
  arrange(desc(tf_idf))

Visualizing high tf-idf words

act_words %>%
  arrange(desc(tf_idf)) %>%
  mutate(word = factor(word, levels = rev(unique(word)))) %>% 
  group_by(act) %>% 
  top_n(10) %>% 
  ungroup() %>%
  ggplot(aes(word, tf_idf, fill = act)) +
  geom_col(show.legend = FALSE) +
  labs(x = NULL, y = "tf-idf") +
  facet_wrap(~act, ncol = 2, scales = "free") +
  coord_flip()
Selecting by tf_idf

Note about converting to/from other non-tidy formats

Document-term matrix

Topic Modeling

Latent Dirichlet allocation (LDA)

Preparing for LDA

data("AssociatedPress")
AssociatedPress
<<DocumentTermMatrix (documents: 2246, terms: 10473)>>
Non-/sparse entries: 302031/23220327
Sparsity           : 99%
Maximal term length: 18
Weighting          : term frequency (tf)

Two-topic Latent Dirichlet allocation (LDA) model

require(topicmodels)
ap_lda <- 
  AssociatedPress %>%
  LDA(k = 2, control = list(seed = 1234))  
ap_lda
A LDA_VEM topic model with 2 topics.

Per-topic-per-word probabilities

ap_topics <- tidy(ap_lda, matrix = "beta")
ap_topics

Top 10 per-topic-per-word probabilities (\(\beta\))

ap_top_terms <- ap_topics %>%
  group_by(topic) %>%
  top_n(10, beta) %>%
  ungroup() %>%
  arrange(topic, -beta)
ap_top_terms %>%
  mutate(term = reorder(term, beta)) %>%
  ggplot(aes(term, beta, fill = factor(topic))) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~ topic, scales = "free") +
  coord_flip()

Words with greatest difference in \(\beta\) between topics

beta_spread <- ap_topics %>%
  mutate(topic = paste0("topic", topic)) %>%
  spread(topic, beta) %>%
  filter(topic1 > .001 | topic2 > .001) %>%
  mutate(log_ratio = log2(topic2 / topic1)) %>%
  arrange(desc(log_ratio))
beta_spread

Per-document classification

scenes_gamma <- tidy(ap_lda, matrix = "gamma")
scenes_gamma %>% 
  arrange(document, gamma)

Let’s investigate a few interesting documents

# investigate document 3 & 6
tidy(AssociatedPress) %>%
  filter(document == 3) %>%
  arrange(desc(count))

Modeling Topics as Acts in Macbeth…?

# convert to Document Term Matrix
ModernMacbeth_DTM <- 
  ModernMacbeth %>%
  mutate(act_scene = gsub(pattern = "\\.txt", replacement = "", x = doc_id)) %>%
  unnest_tokens(output = word, input = text) %>%
  mutate(word = gsub(pattern = "’", replacement = "'", x = word)) %>%
  anti_join(stop_words, by = c("word" = "word")) %>%     
  count(act_scene, word, sort = TRUE) %>%   
  cast_dtm(document = act_scene, term = word, value = n)
# LDA topic model (5 topics)
scenes_lda <- 
  ModernMacbeth_DTM %>%
  LDA(k = 5, control = list(seed = 380))  
# Per-topic-per-word probabilities 
scene_topics <- tidy(scenes_lda, matrix = "beta")
scene_topics
# Visualize top per-topic-per-word probabilities
top_terms <- 
  scene_topics %>%
  group_by(topic) %>%
  top_n(5, beta) %>%
  ungroup() %>%
  arrange(topic, -beta)
top_terms %>%
  mutate(term = reorder(term, beta)) %>%
  ggplot(aes(term, beta, fill = factor(topic))) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~ topic, scales = "free") +
  coord_flip()

# Per-document classification
scenes_gamma <- tidy(scenes_lda, matrix = "gamma")
scenes_gamma %>% 
  arrange(document, gamma)
# Topics vs Acts?
scenes_gamma <- 
  scenes_gamma %>%
  separate(col = document, into = c("act", "scene"), sep = "_", convert = TRUE)
scenes_gamma %>%
  ggplot(aes(factor(topic), gamma)) +
  geom_boxplot() +
  facet_wrap(~ act)

Scene alignment to topics

Road map (again)

LS0tCnRpdGxlOiAiVGV4dCBhcyBEYXRhIgpzdWJ0aXRsZTogIk1EU1IgQ2ggMTUgPGJyPiBUZXh0IE1pbmluZyB3aXRoIFIgKFNpbGdlICYgUm9iaW5zb24sIDIwMTkpIgpvdXRwdXQ6IAogIHNsaWR5X3ByZXNlbnRhdGlvbjogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQgIAotLS0KCgpgYGB7ciBGcm9udCBNYXR0ZXIsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KIyBjbGVhbiB1cCBSIGVudmlyb25tZW50CnJtKGxpc3QgPSBscygpKQoKIyBnbG9iYWwgb3B0aW9ucwprbml0cjo6b3B0c19jaHVuayRzZXQoZXZhbD1UUlVFLCBpbmNsdWRlPVRSVUUpCm9wdGlvbnMoZGlnaXRzPTQpCgojIGxvYWQgYWxsIHBhY2thZ2VzIGhlcmUKbGlicmFyeShtZHNyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHJlYWR0ZXh0KQpsaWJyYXJ5KHRpZHl0ZXh0KQpsaWJyYXJ5KHdvcmRjbG91ZCkKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeSh0b3BpY21vZGVscykKCgojIGlucHV0cyBzdW1tYXJ5CiMgZGF0YSgiTWFjYmV0aF9yYXciKSAgICMgTURTUiBiYWNrdXAgdmVyc2lvbiBvZiBNYWNiZXRoCmRhdGEoInN0b3Bfd29yZHMiKSAgICAjIGZyb20gYHRpZHl0ZXh0YApkYXRhKCJzZW50aW1lbnRzIikgICAgIyBmcm9tIGB0aWR5dGV4dGAKZGF0YSgiQXNzb2NpYXRlZFByZXNzIikgICMgZnJvbSAndG9waWNtb2RlbHMnCgpgYGAKCgojIEFnZW5kYQoKLSBSZWd1bGFyIGV4cHJlc3Npb25zIAotIEJhc2ljIHRleHQgYW5hbHlzaXMKICAgIC0gY29ycG9yYQogICAgLSB3b3JkIGNsb3VkcwogICAgLSBzZW50aW1lbnQgYW5hbHlzaXMKLSBJbnRlcm1lZGlhdGUKICAgIC0gdGVybSBmcmVxdWVuY3kgeCBpbnZlcnNlIGRvY3VtZW50IGZyZXF1ZW5jeSAoKnRmLWlkZiopCiAgICAtIExhdGVudCBEaXJpY2hsZXQgYWxsb2NhdGlvbiAoTERBOyB0b3BpYyBtb2RlbGluZykKCiMjIyMgQW5ub3VuY2VtZW50cwoKLSBGaW5hbCBQcm9qZWN0IHVwZGF0ZQotIE1EU1IgQ2ggMTUgcHJvZ3JhbW1pbmcgbm90ZWJvb2sgYXNzaWduZWQKLSBCZWNrbWFuJ3Mgb2ZmaWNlIGhvdXIgY2FuY2VsbGVkIG9uIFRodXJzIDQvNCAoc2VlIHN5bGxhYnVzIGZvciBvdGhlciBvcHRpb25zKQotIE1EU1IgQ2ggMTUgRXhlcmNpc2VzIGR1ZSA0LzE0IGJlZm9yZSBtaWRuaWdodAotIExvdHMgb2YgZXJyYXRhL3RpcHMgdGhpcyBjaGFwdGVyCgojIyMjIE1EU1IgQ2ggMTUgRXJyYXRhIC8gVGlwcwoKLSBTb21lIHNlY3Rpb25zIGRvbid0IHJlcXVpcmUgcHJvZ3JhbW1pbmcsIGJ1dCBwbGVhc2Ugc3RpbGwgaW5jbHVkZSB0aGUgaGVhZGVycyBmb3IgbmF2aWdhdGlvbiBwdXJwb3NlcwotIHAuIDM2MTogTm90ZS0tYGRhdGEoIkRhdGFTY2llbmNlUGFwZXJzIilgIHNob3VsZCBhcHBlYXIgQk9USCBpbiBmcm9udCBtYXR0ZXIgKHBlciBzdHlsZSBndWlkZSkgQU5EIGFzIGEgY29kZSBjaHVuayBpbiBzZXF1ZW5jZSB3aXRoIHRoZSBvdGhlciBNRFNSIGNvZGUgc28geW91ciBzZWN0aW9uIDE1LjIgcmVzdWx0cyBpbiB0aGUgcHJvZ3JhbW1pbmcgbm90ZWJvb2sgd2lsbCBtYXRjaCB0aGUgTURTUiBib29rIHJhdGhlciB0aGFuIHVzaW5nIG5ldyBkYXRhIHF1ZXJpZWQgZnJvbSBhclhpdiBhbHNvIGxvYWRlZCBvbiBwLiAzNjEgdW5kZXIgdGhlIG9iamVjdCBuYW1lIGBEYXRhU2NpZW5jZVBhcGVyc2AKLSBwLiAzNjU6IHlvdXIgd29yZCBjbG91ZCB3b24ndCBiZSBpZGVudGljYWwgYW5kIG1heSBldmVuIGV4Y2x1ZGUgImRhdGEiIGlmIGl0IGRvZXNuJ3QgZml0IG9uIHRoZSBwYWdlCi0gcC4gMzY4OiBXaWtpcGVkaWEgY2hhbmdlZCB0aGUgdGFibGVzIGFsbCBhcm91bmQuIFVzZSBgVGFibGVbWzRdXWAgd2hlbiBzY3JhcGluZzsgYFRpdGxlYCBpcyBub3cgYFNvbmdgOyB0aGUgcmVzdWx0cyB3b24ndCBleGFjdGx5IG1hdGNoIGJ1dCB5b3UnbGwgYmUgYWJsZSB0byB3b3JrIGl0IG91dC4KLSBwLiAzNjk6IGNoYW5nZSBgMjVgIHRvIGAxNWAgKHRoZSByZXN1bHQgaXMgcXVpdGUgYSBkaWZmZXJlbnQgbWVzc2FnZSEpCi0gcC4gMzcwOiB5b3UgZG9uJ3QgbmVlZCBhIFR3aXR0ZXIgYWNjb3VudCwgdGhlIHByb3ZpZGVkIGNyZWRlbnRpYWxzIHdvcmsuLi4gaGVyZSB0aGV5IGFyZToKICAgIC0gY29uc3VtZXJfa2V5ID0gInUyVXRoamJLNllIeVFTcDRzUGs2eWpzdVYiIAogICAgLSBjb25zdW1lcl9zZWNyZXQgPSAic0M0bWpkMldNRTVuSDFGb1dlU1R1U3k3SkNQNURIak50VFlVMVg2QndRMXZQWjBqM3YiIAogICAgLSBhY2Nlc3NfdG9rZW4gPSAiMTM2NTYwNjQxNC03dlBmUHhTdFlOcTZrV0VBVFFsVDhIWkJkNEc4M0JCY1g0Vm9TOVQiIAogICAgLSBhY2Nlc3Nfc2VjcmV0ID0gIjBoSnE5S1lDM2VCUnVaekpxU2FjbXRKNFBOSjd0TkxrR3JRclZsMDBKSGlycyIKLSBwLiAzNzI6IHlvdSdsbCBuZWVkIHRvIGxvYWQgdGhlIGBSU1FMaXRlYCBsaWJyYXJ5Ci0gcC4gMzczOiB5b3UgY2FuIHNraXAgdGhlIGBnZW9jb2RlKC4uLilgIGZ1bmN0aW9uIGZyb20gdGhlIGBnZ21hcGAgcGFja2FnZSBhbmQganVzdCBhc3NpZ24gYGxvbmAgYW5kIGBsYXRgIGRpcmVjdGx5LiAgWW91IGNhbiBldmVuIGxvb2sgdXAgdGhlIGNvb3JkaW5hdGVzIGZvciBTdGF0ZSBDb2xsZWdlIGFuZCBzdWJzdGl0dXRlIHRoYXQgaWYgeW91IGxpa2UuICBPZGRseSBlbm91Z2gsIHlvdSBtaWdodCBoYXZlIHRvIGdpdmUgYSBjcmVkaXQgY2FyZCBudW1iZXIgdG8gR29vZ2xlIGlmIHlvdSB3YW50IHRvIHVzZSBgZ2VvY29kZSgpYCBzbyBmZWVsIGZyZWUgdG8gbW9kaWZ5IHlvdXIgY29kZSB0byBhdm9pZCB0aGF0IHN0ZXAuCgoKIyBSb2FkIG1hcAoKPGNlbnRlcj4KCiFbaHR0cHM6Ly93d3cudGlkeXRleHRtaW5pbmcuY29tL3RvcGljbW9kZWxpbmcuaHRtbF0odGlkeXRleHRGbG93LnBuZyl7d2lkdGg9OTUlfQoKPC9jZW50ZXI+CgoKCgojIFByb2plY3QgR3V0ZW5iZXJnIAoKLSA8aHR0cHM6Ly93d3cuZ3V0ZW5iZXJnLm9yZy8+Ci0gUHJvamVjdCBHdXRlbmJlcmcgaXMgYSBtYXNzaXZlIGZyZWUgb25saW5lIGxpYnJhcnkKLSBmdWxsLXRleHQgb2YgYWxtb3N0IDYwLDAwMCBmcmVlIGVib29rcyAoZS5nLiwgZXhwaXJlZCBjb3B5cmlnaHQpCiAgICAtIEphbmUgQXVzdGluCiAgICAtIENoYXJsZXMgRGlja2VucwogICAgLSBNYXJrIFR3YWluCiAgICAtIEFydGh1ciBDb25hbiBEb3lsZQogICAgLSBPc2NhciBXaWxkZQogICAgLSAqKkJpbGwgU2hha2VzcGVhcmUqKgogICAgLSAoYW5kIGxvYWRzIG1vcmUuLi4pCiAgICAtIE5ldyBvbiAzLzI5LzIwMTktLVRoZSBBbWVyaWNhbiBCZWUgSm91cm5hbCEKCgojIE1hY2JldGggU3VtbWFyeQoKT3VyICJtdXNlIiBmb3IgdGhpcyBmaXJzdCBwb3J0aW9uIGlzIHRoZSBmYW1vdXMgcGxheSAqKk1hY0JldGgqKiBieSBXaWxsaWFtIFNoYWtlc3BlYXJlLiAgSWYgeW91IGFyZW4ndCBmYW1pbGlhciB3aXRoIHRoZSBwbGF5LCBoZXJlIGlzIGEgcXVpY2sgc3VtbWFyeToKCi0gOSBtaW51dGVzOiA8aHR0cHM6Ly95b3V0dS5iZS91ekF1anlXcEtfcz4KLSAyIG1pbnV0ZXMgKDEpOiA8aHR0cHM6Ly95b3V0dS5iZS93RHBtWmJmRFRKST4KLSAyIG1pbnV0ZXMgKDIpOiA8aHR0cHM6Ly92aW1lby5jb20vMTI0MDgyNTc3PgotIDIgbWludXRlcyAoMyk6IDxodHRwczovL3lvdXR1LmJlL0Y1bmx4Mlh6UC00P3Q9Mz4KLSAyIG1pbnV0ZXMgKDQpOiA8aHR0cHM6Ly95b3V0dS5iZS9US1lyWlNKMnJVbz4KCgojIFRleHQgYXMgZGF0YSAKCi0gTWFjYmV0aCB0ZXh0OiA8aHR0cDovL3d3dy5ndXRlbmJlcmcub3JnL2NhY2hlL2VwdWIvMTEyOS9wZzExMjkudHh0PgotIFE6IFdoYXQgYXJlIHNvbWUgb2YgdGhlIGNoYWxsZW5nZXMgeW91IHdvdWxkIGV4cGVjdCB3aGVuIHdvcmtpbmcgd2l0aCB0ZXh0IGFzIGRhdGE/CgoKIyBNYWNiZXRoIHRleHQgZGF0YSBpbnRha2UKCi0gUHJldHR5IGZhc3QhCi0gSG93IGRvZXMgaXQgbG9vaz8KICAgIC0gUTogTG9vayBjbG9zZWx5LCBDYW4geW91IHJlYWQgdGhpcz8KICAgIC0gUTogSG93IGNhbiB3ZSBtYWtlIHRoZSB0ZXh0IG1vcmUgdW5kZXJzdGFuZGFibGU/CgpgYGB7cn0KbWFjYmV0aF91cmwgPC0gImh0dHA6Ly93d3cuZ3V0ZW5iZXJnLm9yZy9jYWNoZS9lcHViLzExMjkvcGcxMTI5LnR4dCIKTWFjYmV0aF9yYXcgPC0gUkN1cmw6OmdldFVSTChtYWNiZXRoX3VybCkKIyBNYWNiZXRoX3JhdwpgYGAKCgojIFRleHQgYXMgZGF0YQoKLSBIdW1hbnM6IAogICAgLSBleGNlbGxlbnQgYXQgKnVuZGVyc3RhbmRpbmcqIHRleHQKICAgIC0gbm90IGFzIGdvb2QgYXQgKnN0b3JpbmcqIHRleHQKLSBDb21wdXRlcnM6IAogICAgLSBleGNlbGxlbnQgYXQgKnN0b3JpbmcqIHRleHQKICAgIC0gbm90IGFzIGdvb2QgYXQgKnVuZGVyc3RhbmRpbmcqIHRleHQgCi0gSHVtYW4gKyBDb21wdXRlcj8/CiAgICAtIGV4Y2VsbGVudCBhdCB1bmRlcnN0YW5kaW5nICYgc3RvcmluZyB0ZXh0PwogICAgLSBub3Qgc28gZ29vZCBhdCBzdG9yaW5nICYgdW5kZXJzdGFuZGluZyB0ZXh0PwogICAgLSAoZGVwZW5kcyBvbiB0aGUgaHVtYW4gJiB0aGUgY29tcHV0ZXIuLi4/KQoKCiMgQmFjayB0byBNYWNiZXRoCgotIFdlIG5lZWQgdG8gc3BsaXQgdGhlIG9uZSBnaWFudCBzdHJpbmcgb2YgYE1hY2JldGhfcmF3YCBldmVyeXdoZXJlIHRoYXQgd2Ugc2VlIHRoZSBwYXR0ZXJuIGBcclxuYCAobWFya2luZyBlbmQgb2YgbGluZSkKLSBROiBXaGF0IGNsYXNzIG9mIG9iamVjdCBkbyB3ZSBoYXZlIG5vdz8KLSBROiBJcyBpdCBiZXR0ZXI/CgoKYGBge3J9Cm1hY2JldGhfdG1wIDwtIHN0cnNwbGl0KE1hY2JldGhfcmF3LCAiXHJcbiIpCnN0cihtYWNiZXRoX3RtcCkKCgptYWNiZXRoIDwtIHN0cnNwbGl0KE1hY2JldGhfcmF3LCAiXHJcbiIpW1sxXV0KbGVuZ3RoKG1hY2JldGgpCgpoZWFkKG1hY2JldGgpCmBgYAoKCiMgVGhlIHNldCB1cC4uLgoKLSBMZXQncyBzYXkgeW91IGVucm9sbGVkIGluIFRIRUEgMTAyIGZvciBhIEdBIGNyZWRpdDogRnVuZGFtZW50YWxzIG9mIEFjdGluZyAgCi0gVGhlIGNsYXNzIHdpbGwgY2hvb3NlIGEgcGxheSBieSBTaGFrZXNwZWFyZSBhbmQgdGhlbiBldmVyeW9uZSB3aWxsIHZvbHVudGVlciBmb3IgdGhlIHZhcmlvdXMgcGFydHMuICAKLSBUaGVyZSdzIHNvbWVvbmUgaW4gdGhlIGNsYXNzIHlvdSB3YW50IHRvIGltcHJlc3MgCiAgICAtIFlvdSB3YW50IGEgcGFydCB0aGF0IGlzIGltcG9ydGFudCwgCiAgICAtIGJ1dCB5b3UgZG9uJ3Qgd2FudCAqdG9vKiBtYW55IGxpbmVzIHRvIG1lbW9yaXplLi4uCgoKIyBNYW51YWwgdGV4dCBhbmFseXNpcwoKLSBIb3cgd291bGQgeW91IGNvdW50IHVwIHRoZSBsaW5lcyBpZiB5b3UganVzdCBoYXZlIGEgcGh5c2ljYWwgY29weSBvZiB0aGUgcGxheT8KICAgIC0gUTogRm9yIG9uZSBjaGFyYWN0ZXI/CiAgICAtIFE6IEZvciBhbGwgY2hhcmFjdGVycz8KICAgIC0gUTogRm9yIGFsbCAzNyBvZiBTaGFrZXNwZWFyZSdzIHBsYXlzPwoKYGBge3J9Cm1hY2JldGhbMjk1OjMxMF0KCmBgYAoKCiMgTWFudWFsIHRleHQgYW5hbHlzaXMKCi0gSWYgeW91ICoqaGFkKiogdG8gZG8gaXQgbWFudWFsbHksIHlvdSdkIHByb2JhYmx5IGZsaXAgdGhyb3VnaCB0aGUgcGFnZXMgYW5kIG1ha2UgYSB0YWxseSAob24gYSBzZXBhcmF0ZSBzaGVldCkgZm9yIGVhY2ggbGluZQotIE5vdyB0aGF0IHlvdSBrbm93IHlvdSBjYW4gZmluZCBTaGFrZXNwZWFyZSdzIHBsYXlzIHRocm91Z2ggUHJvamVjdCBHdXRlbmJlcmcsIHlvdSBjYW4gcHJvYmFibHkgYWNjZXNzIGVib29rcyBmb3IgYWxsIG9mIHRoZW0KICAgIC0gUTogV2hhdCdzIHRoZSBhcHByb2FjaD8KICAgIC0gUTogU2Nyb2xsaW5nIGluc3RlYWQgb2YgImZsaXBwaW5nIiBidXQgb3RoZXJ3aXNlIHNhbWU/CiAgICAtIFE6IFNvbWV0aGluZyBlbHNlPwoKCiMgUmVndWxhciBFeHByZXNzaW9ucyAoUmVnRXgpCgotIEh1bWFucyBhcmUgZ29vZCBhdCB1bmRlcnN0YW5kaW5nIHRleHQsIGJ1dCBiYWQgYXQgc3RvcmluZyBsb3RzIG9mIHRleHQgYW5kIHNsb3cgYXQgc2VhcmNoaW5nCi0gQ29tcHV0ZXJzIGFyZSBnb29kIGF0IHN0b3JpbmcgJiBzZWFyY2hpbmcsIGJ1dCBub3QgZ29vZCBhdCAidW5kZXJzdGFuZGluZyIgaXQuCi0gKipSZWd1bGFyIGV4cHJlc3Npb25zKioKICAgIC0gQ29tcHV0ZXIgc3RvcmVzIHRoZSB0ZXh0IChnb29kIGF0IHRoaXMpCiAgICAtIEh1bWFuIGlkZW50aWZpZXMgcGF0dGVybiAoZ29vZCBhdCB0aGlzKQogICAgLSBIdW1hbiB0cmFuc2xhdGVzIHBhdHRlcm4gc28gY29tcHV0ZXIgY2FuIHNlYXJjaCBmb3IgaXQgKHRha2VzIHByYWN0aWNlLi4uKQogICAgLSBDb21wdXRlciBzZWFyY2hlcyB0aGUgdGV4dCAoZ29vZCBhdCB0aGlzKQotIFJTdHVkaW8gQ2hlYXRzaGVldHM6IDxodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvPgoKCiMgYGdyZXAoIClgICYgYGdyZXBsKCApYAoKLSB0aGVzZSBmdW5jdGlvbnMgbG9vayBmb3IgdGhlIG5lZWRsZSAocGF0dGVybikgaW4gdGhlIGhheXN0YWNrIChjaGFyYWN0ZXIgdmVjdG9yKQogICAgLSBuZWVkbGUvcGF0dGVybjogYE1BQ0JFVEgiYCB3aXRoIHR3byBsZWFkaW5nIHNwYWNlcwogICAgLSBoYXlzdGFjay90ZXh0OiAgYG1hY2JldGhgIGNoYXJhY3RlciB2ZWN0b3Igb2JqZWN0Ci0gUTogV2hhdCdzIHJldHVybmVkIGluIGVhY2ggY2FzZT8KICAgIC0gQXJlIHRoZXNlIE1hY2JldGgncyBsaW5lcz8KICAgIC0gQWxsIG1lbnRpb25zIG9mIE1hY2JldGhzJyBuYW1lPwogICAgLSBBcmUgd2UgZG9uZT8KLSBROiBIb3cgYXJlIHdlIGRvaW5nPwogICAgLSBDYW4gd2UgaW1wcm92ZSBpdCBmdXJ0aGVyPwogICAgLSBDb21wYXJlIHdpdGggQ3RybCArIEYgb24gdGhlIHdlYnNpdGU/CgoKYGBge3J9Cm1hY2JldGhfbGluZXMgPC0gZ3JlcCgiTUFDQkVUSCIsIG1hY2JldGgpCiMgbWFjYmV0aF9saW5lcyA8LSBncmVwKCJNQUNCRVRIIiwgbWFjYmV0aCwgdmFsdWUgPSBUUlVFKQojIG1hY2JldGhfbGluZXMgPC0gZ3JlcGwoIk1BQ0JFVEgiLCBtYWNiZXRoKQoKbGVuZ3RoKG1hY2JldGhfbGluZXMpCmhlYWQobWFjYmV0aF9saW5lcykKYGBgCgoKYGBge3IgZXZhbD1GQUxTRX0KaWRlbnRpY2FsKGMoMTozKSwgYygxTCwgMkwsIDNMKSkgICMgYXJlIHRoZXNlIHRoZSBzYW1lPwppZGVudGljYWwoYygxOjMpLCBjKDEsIDIsIDMpKSAgICAgIyBgaWRlbnRpY2FsYCBpcyBQSUNLWQoKIyBhcmUgdGhlc2UgdGhlIHNhbWU/CmlkZW50aWNhbChtYWNiZXRoW2dyZXAoIk1BQ0JFVEgiLCBtYWNiZXRoKV0sICAgCiAgICAgICAgICBtYWNiZXRoW2dyZXBsKCJNQUNCRVRIIiwgbWFjYmV0aCldKQpgYGAKCjwhLS0gRGF5MiAtLT4KCgojIFJlZmluaW5nIG91ciBSZWdFeAoKLSBIb3cgZG8geW91IGlkZW50aWZ5IGEgc3BlYWtlcidzIGxpbmVzPyAoSHVtYW5zIGFyZSBnb29kIGF0IHRoaXMsIG5vdCBjb21wdXRlcnMpCiAgICAtIEFsbCBDQVBJVEFMIExFVFRFUlMKICAgIC0gVHdvIGxlYWRpbmcgc3BhY2VzCiAgICAtIGVuZHMgd2l0aCBwZXJpb2QKLSBROiBDbG9zZSwgYnV0IHdoYXQncyB3cm9uZyBoZXJlPwoKYGBge3J9Cm1hY2JldGhfbGluZXMgPC0gZ3JlcCgiICBNQUNCRVRILiIsIG1hY2JldGgsIHZhbHVlID0gVFJVRSkKCmxlbmd0aChtYWNiZXRoX2xpbmVzKQpoZWFkKG1hY2JldGhfbGluZXMpCmBgYAoKCiMgUHJvYmxlbSB3aXRoIHBlcmlvZCAoYC5gKQoKLSBJbiBSZWdFeCwgdGhlIHBlcmlvZCBpcyBhIGEgbWV0YWNoYXJhY3RlciB0aGF0IG1hdGNoZXMgQU5ZIGNoYXJhY3RlcgotIGBcXC5gIGlzIHJlcXVpcmVkIHRvIGxpdGVyYWxseSBzZWFyY2ggZm9yIHBlcmlvZHMgaW4gb3VyIFJlZ0V4IHBhdHRlcm4KICAgIC0gYFxcYCBpcyBjYWxsZWQgYW4gImVzY2FwZSIgCiAgICAtIGVzY2FwZSBpcyByZXF1aXJlZCBmb3IgYW55IHBhcnQgb2YgYSBSZWdFeCB0aGF0IGhhcyBzcGVjaWFsIG1lYW5pbmcsIGJ1dCB5b3UganVzdCB3YW50IHRvIHNlYXJjaCBsaXRlcmFsbHkKCgpgYGB7cn0KIyBhbnl0aGluZyB3aXRoICJNQUMiIGFuZCB0aGVuIGFub3RoZXIgY2hhcmFjdGVyCmdyZXAoIk1BQy4iLCBtYWNiZXRoLCB2YWx1ZSA9IFRSVUUpICU+JQogIGhlYWQoKQoKIyBNQUNCRVRILgpncmVwKCJNQUNCRVRIXFwuIiwgbWFjYmV0aCwgdmFsdWUgPSBUUlVFKSAlPiUgaGVhZCgyMCkKCmBgYAoKIyBTaW1wbGUgUmVnRXggVG9vbHMKCi0gYHxgOiBhbHRlcm5hdGlvbi0tc2VhcmNoIGZvciBhIGZldyBzcGVjaWZpYyBhbHRlcm5hdGl2ZXMKICAgIC0gYCJNQUNbQnxEXSJgIHdvdWxkIG1hdGNoIGFsbCBzdHJpbmdzIHRoYXQgaW5jbHVkZSAiTUFDQiIgT1IgIk1BQ0QiCi0gYFsgXWA6IGNoYXJhY3RlciBzZXRzLS1zcXVhcmUgYnJhY2tldHMgZGVmaW5lIHNldHMgb2YgY2hhcmFjdGVycyB0byBtYXRjaAogICAgLSBgIk1BQ1tDLVpdImAgd291bGQgbWF0Y2ggIk1BQyIgZm9sbG93ZWQgYnkgYW55IGNhcGl0YWwgbGV0dGVyIGZyb20gIkMiIHRocm91Z2ggIloiCi0gYF5gIG9yIGAkYDogYW5jaG9ycy0tc2VhcmNoIGZvciBwYXR0ZXJuIHN0cmljdGx5IGF0IHRoZSBiZWdpbm5pbmcgKGBeYCkgb3IgZW5kIChgJGApIG9mIHN0cmluZwogICAgLSBgIl5BQ1QiYCB3b3VsZCBtYXRjaCBhbGwgbGluZXMgc3RyaWN0bHkgYmVnaW5uaW5nIHdpdGggIkFDVCIKICAgIC0gYCJNQUNCRVRIJCJgIHdvdWxkIG1hdGNoIGFsbCBsaW5lcyBzdHJpY3RseSBlbmRpbmcgd2l0aCAiTUFDQkVUSCIgKG5vIHB1bmN0dWF0aW9uKQotIGA/YCBvciBgKmAgb3IgYCtgOiByZXBldGl0aW9ucwogICAgLSBgIl4gP01BQyJgIHdvdWxkIG1hdGNoIGlmIHN0cmluZyBiZWdpbnMgd2l0aCB6ZXJvIG9yIG9uZSBsZWFkaW5nIHNwYWNlcyBmb2xsb3dlZCBieSAiTUFDIgogICAgLSBgIl4gKk1BQyJgIHdvdWxkIG1hdGNoIGlmIHN0cmluZyBiZWdpbnMgd2l0aCB6ZXJvIG9yIG1vcmUgbGVhZGluZyBzcGFjZXMgZm9sbG93ZWQgYnkgIk1BQyIKICAgIC0gYCJeICtNQUMiYCB3b3VsZCBtYXRjaCBpZiBzdHJpbmcgYmVnaW5zIHdpdGggb25lIG9yIG1vcmUgbGVhZGluZyBzcGFjZXMgZm9sbG93ZWQgYnkgIk1BQyIKICAgIC0gYCJeIHszfU1BQyJgIHdvdWxkIG1hdGNoIGlmIHN0cmluZyBiZWdpbnMgd2l0aCBleGFjdGx5IDMgbGVhZGluZyBzcGFjZXMgZm9sbG93ZWQgYnkgIk1BQyIKCmBgYHtyfQojIGFsdGVybmF0aW9uIHdpdGggYHxgCmdyZXAoIk1BQ1tCfERdIiwgbWFjYmV0aCwgdmFsdWUgPSBUUlVFKSAlPiUgaGVhZCgpCgojICJNQUMiIGZvbGxvd2VkIGJ5IGFueSBjYXBpdGFsIGxldHRlciBmcm9tICJDIiB0aHJvdWdoICJaIgpncmVwKCJNQUNbQy1aXSIsIG1hY2JldGgsIHZhbHVlID0gVFJVRSkgJT4lIGhlYWQoMTApCgojIHNlYXJjaCBmb3IgYmVnaW5uaW5nIG9mIGVhY2ggYWN0IGluIHRoZSBwbGF5IChgXmAgZ29lYSBhdCBiZWdpbm5pbmcpCmdyZXAoIkFDVCIsIG1hY2JldGgsIHZhbHVlID0gVFJVRSkgJT4lIGhlYWQoMTApCmdyZXAoIl5BQ1QiLCBtYWNiZXRoLCB2YWx1ZSA9IFRSVUUpICU+JSBoZWFkKDEwKQoKIyBzdHJpbmdzIHN0cmljdGx5IGVuZGluZyBpbiAiTUFDQkVUSCIgKGAkYCBnb2VzIGF0IGVuZCkKZ3JlcCgiTUFDQkVUSCQiLCBtYWNiZXRoLCB2YWx1ZSA9IFRSVUUpICU+JSBoZWFkKDEwKQoKIyByZXBldGl0aW9ucwpncmVwKCJeID9NQUMiLCBtYWNiZXRoLCB2YWx1ZSA9IFRSVUUpICU+JSBoZWFkKCkgICMgemVybyBvciBvbmUgbGVhZGluZyBzcGFjZXMKZ3JlcCgiXiAqTUFDIiwgbWFjYmV0aCwgdmFsdWUgPSBUUlVFKSAlPiUgaGVhZCgpICAjIHplcm8gb3IgbW9yZSBsZWFkaW5nIHNwYWNlcwpncmVwKCJeICtNQUMiLCBtYWNiZXRoLCB2YWx1ZSA9IFRSVUUpICU+JSBoZWFkKCkgICMgb25lIG9yIG1vcmUgbGVhZGluZyBzcGFjZXMKZ3JlcCgiXiB7Mn1NQUMiLCBtYWNiZXRoLCB2YWx1ZSA9IFRSVUUpICU+JSBoZWFkKCkgICMgZXhhY2x0eSB0d28gbGVhZGluZyBzcGFjZXMKZ3JlcCgiXiB7M31NQUMiLCBtYWNiZXRoLCB2YWx1ZSA9IFRSVUUpICU+JSBoZWFkKCkgICMgZXhhY3RseSB0aHJlZSBsZWFkaW5nIHNwYWNlcwoKYGBgCgojIFNwZWFrZXIgZnJlcXVlbmN5IGluIE1hY2JldGggCgotIHJlY2FsbDogeW91IHdhbnRlZCBhbiBpbXBvcnRhbnQgY2hhcmFjdGVyLCBidXQgbm90IGEgaHVnZSBudW1iZXIgb2YgbGluZXMgdG8gbWVtb3JpemUKICAgIC0gd2FudCBhIGRlY2VudCBudW1iZXIgb2YgbGluZXMKICAgIC0gImltcG9ydGFudCIgY291bGQgbWVhbiBpbnZvbHZlZCBmb3IgbW9zdCBvZiB0aGUgcGxheSAobm90IGtpbGxlZCBpbW1lZGlhdGVseSkKICAgIC0gUTogV2hvIG1lZXRzIG91ciBjcml0ZXJpYT8KICAgIAoKYGBge3J9Ck1hY2JldGggPC0gZ3JlcGwoIiAgTUFDQkVUSFxcLiIsIG1hY2JldGgpCk1hY2R1ZmYgPC0gZ3JlcGwoIiAgTUFDRFVGRlxcLiIsIG1hY2JldGgpCkxhZHlNYWNiZXRoIDwtIGdyZXBsKCIgIExBRFkgTUFDQkVUSFxcLiIsIG1hY2JldGgpCkxhZHlNYWNkdWZmIDwtIGdyZXBsKCIgIExBRFkgTUFDRFVGRlxcLiIsIG1hY2JldGgpCkJhbnF1byA8LSBncmVwbCgiICBCQU5RVU9cXC4iLCBtYWNiZXRoKQpEdW5jYW4gPC0gZ3JlcGwoIiAgRFVOQ0FOXFwuIiwgbWFjYmV0aCkKCgpzcGVha2VyX2ZyZXEgPC0gZGF0YS5mcmFtZShNYWNiZXRoLCBNYWNkdWZmLCBMYWR5TWFjYmV0aCwgTGFkeU1hY2R1ZmYsIEJhbnF1bywgRHVuY2FuKSAlPiUKICBtdXRhdGUobGluZSA9IDE6bGVuZ3RoKG1hY2JldGgpKSAlPiUKICBnYXRoZXIoa2V5ID0gImNoYXJhY3RlciIsIHZhbHVlID0gInNwZWFrIiwgLWxpbmUpICU+JQogIG11dGF0ZShzcGVhayA9IGFzLm51bWVyaWMoc3BlYWspKSAlPiUKICBmaWx0ZXIobGluZSA+IDIxOCAmIGxpbmUgPCAzMTcyKQoKZ2xpbXBzZShzcGVha2VyX2ZyZXEpCmBgYAoKCgpgYGB7cn0KIyBmaW5kIHRoZSBhY3RzIHRoYXQgc3ViZGl2aWRlIHRoZSBwbGF5CmFjdHNfaWR4IDwtIGdyZXAoIl5BQ1QgIiwgbWFjYmV0aCkKYWN0c19sYWJlbHMgPC0gc3RyX2V4dHJhY3QobWFjYmV0aFthY3RzX2lkeF0sICJeQUNUIFtJfFZdKyIpICAjIFtJfFZdKyBJLCBJSSwgSUlJLCBJViwgVgphY3RzIDwtIGRhdGEuZnJhbWUobGluZSA9IGFjdHNfaWR4LCBsYWJlbHMgPSBhY3RzX2xhYmVscykKCiMgcGxvdCBzcGVha2VyIGZyZXF1ZW5jaWVzCnNwZWFrZXJfZnJlcSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBsaW5lLCB5ID0gc3BlYWspKSArIAogIGdlb21fc21vb3RoKGFlcyhjb2xvciA9IGNoYXJhY3RlciksIG1ldGhvZCA9ICJsb2VzcyIsIHNlID0gMCkgKyAKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBhY3RzX2lkeCwgY29sb3IgPSAiZGFya2dyYXkiLCBsdHkgPSAzKSArIAogIGdlb21fdGV4dChkYXRhID0gYWN0cywgYWVzKHkgPSAwLjA4NSwgbGFiZWwgPSBsYWJlbHMpLCAKICAgICAgICAgICAgaGp1c3QgPSAibGVmdCIsIGNvbG9yID0gImRhcmtncmF5IikgKyAKICB5bGltKGMoMCwgTkEpKSArIAogIHhsYWIoIkxpbmUgTnVtYmVyIikgKyAKICB5bGFiKCJQcm9wb3J0aW9uIG9mIFNwZWVjaGVzIikKYGBgCgoKCiMgRnVydGhlciBBbmFseXNpcwoKLSBHZXR0aW5nIGNsb3Nlci4uLiBidXQgd2Ugc2hvdWxkIHByb2JhYmx5IG9wZW4gdXAgdGhlIHNlYXJjaCBhIGxpdHRsZQotICoqUHJvYmxlbSoqOiBXZSByZWFsbHkgb25seSBncmFiYmVkIHRoZSBmaXJzdCBwYXJ0IG9mIGVhY2ggbGluZS4uLiAKICAgIC0gV2hhdCBpZiB3ZSBhY2NpZGVudGFsbHkgY2hvb3NlIGEgY2hhcmFjdGVyIHByb25lIHRvIGxvbmcgc3BlZWNoZXM/IAogICAgLSBNaWdodCBiZSB0b28gbXVjaCB0byBtZW1vcml6ZS4uLiB3ZSBuZWVkIGJldHRlciBpbmZvcm1hdGlvbgotIFE6IEhvdyBkbyB5b3Ugc3VnZ2VzdCB3ZSBhY2Nlc3MgKmVudGlyZSBsaW5lcyogZm9yIGVhY2ggcGFydCBpbiB0aGUgcGxheT8KICAgIC0gSG93IHdvdWxkIHlvdSBkbyBpdCAiYnkgZXllIj8KICAgIC0gSG93IGNhbiB5b3UgdHJhbnNsYXRlIHRoYXQgaW50byBSZWdFeD8KCiMgSW5kZW50aWZ5aW5nIGxpbmVzICYgc3BlYWtlcnMKCi0gUTogV2hhdCBkb2VzIHRoaXMgUmVnRXggcGF0dGVybiBnZXQgZm9yIHVzPyAgYCJcclxuIHsyfVtBLVp8IHwwLTldKlxcLiAiYAotIFE6IFdoYXQncyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSByZXN1bHQgZm9yCiAgICAtIGBzdHJzcGxpdGAgCiAgICAtIGBzdHJpbmdyOjpleHRyYWN0YAogICAgLSBgc3RyaW5ncjo6ZXh0cmFjdF9hbGxgCi0gUTogV2h5IGBbLTFdYCBhdCB0aGUgZW5kIG9mIGBzdHJzcGxpdGAgY29tbWFuZD8KCgpgYGB7cn0KbGluZXMgPC0gc3Ryc3BsaXQoTWFjYmV0aF9yYXcsICJcclxuIHsyfVtBLVp8IHwwLTldKlxcLiAiKVtbMV1dWy0xXQpzcGVha2VycyA8LSBzdHJpbmdyOjpzdHJfZXh0cmFjdF9hbGwoTWFjYmV0aF9yYXcsICIgezJ9W0EtWnwgfDAtOV0qXFwuICIpW1sxXV0KCkFsbE1hY2JldGggPC0gZGF0YS5mcmFtZShzcGVha2VycywgbGluZXMpCgpgYGAKCiMgQ2xlYW4gdXAgdGhlIGxpbmVzCgotIFE6IFdoYXQncyBsZWZ0IHRvIGNsZWFuIHVwPwogICAgLSBTdWdnZXN0aW9ucz8KCmBgYHtyfQpoZWFkKEFsbE1hY2JldGgpCmBgYAoKCiMgQ2xlYW4gdXAgdGhlIGxpbmVzCgotIGBnc3ViYCBpcyBoYW5keSBmb3IgImZpbmQgJiByZXBsYWNlIgotIGlmIHlvdSB3YW50IHRvIHNpbXBseSBjdXQgc29tZSBwYXR0ZXJuIG91dCBvZiB0aGUgdGV4dAogICAgLSBmaW5kIGl0IHdpdGggdGhlIHByb3BlciBSZWdFeAogICAgLSByZXBsYWNlIHdpdGggIiIgKHRoYXQncyBhIHBhdHRlcm4gd2l0aCBub3RoaW5nIGluIGl0ISkKCmBgYHtyfQpBbGxNYWNiZXRoIDwtIAogIEFsbE1hY2JldGggJT4lCiAgbXV0YXRlKGxpbmVzID0gZ3N1YihwYXR0ZXJuID0gIltccnxcbl0qIiwgcmVwbGFjZW1lbnQgPSAiIiwgeCA9IGxpbmVzKSkKCmhlYWQoQWxsTWFjYmV0aCkKYGBgCgojIE1vcmUgY2xlYW4gdXAKCi0gVGhlIGVuZCBvZiBlYWNoIEFjdCBzZWVtcyB0byBpbmNsdWRlIHNvbWV0aGluZyBsaWtlIHRoZSBmb2xsb3dpbmcgdGV4dC4uLgoKYDw8VEhJUyBFTEVDVFJPTklDIFZFUlNJT04gT0YgVEhFIENPTVBMRVRFIFdPUktTIE9GIFdJTExJQU1gCmBTSEFLRVNQRUFSRSBJUyBDT1BZUklHSFQgMTk5MC0xOTkzIEJZIFdPUkxEIExJQlJBUlksIElOQy4sIEFORCBJU2AKYFBST1ZJREVEIEJZIFBST0pFQ1QgR1VURU5CRVJHIEVURVhUIE9GIENBUk5FR0lFIE1FTExPTiBVTklWRVJTSVRZYApgV0lUSCBQRVJNSVNTSU9OLiAgRUxFQ1RST05JQyBBTkQgTUFDSElORSBSRUFEQUJMRSBDT1BJRVMgTUFZIEJFYApgRElTVFJJQlVURUQgU08gTE9ORyBBUyBTVUNIIENPUElFUyAoMSkgQVJFIEZPUiBZT1VSIE9SIE9USEVSU2AKYFBFUlNPTkFMIFVTRSBPTkxZLCBBTkQgKDIpIEFSRSBOT1QgRElTVFJJQlVURUQgT1IgVVNFRGAKYENPTU1FUkNJQUxMWS4gIFBST0hJQklURUQgQ09NTUVSQ0lBTCBESVNUUklCVVRJT04gSU5DTFVERVMgQlkgQU5ZYApgU0VSVklDRSBUSEFUIENIQVJHRVMgRk9SIERPV05MT0FEIFRJTUUgT1IgRk9SIE1FTUJFUlNISVAuPj5gCgotIFE6IFdoZXJlIGRvIHlvdSB0aGluayB0aGF0ICJqdW5rIiBsYW5kZWQgZHVlIHRvIG91ciBhcHByb2FjaCBzbyBmYXI/IAotIFE6IFdoYXQgc2hvdWxkIHdlIGRvPwotIFE6IElzIHRoZXJlIG1vcmUgImp1bmsiIGxpa2UgdGhpcz8KCgojIEFub3RoZXIgYGdzdWJgCgotIHdlJ3JlIG5vdCBnb2luZyB0byBjbGVhbiB1cCAqZXZlcnl0aGluZyosIGJ1dCB3ZSdsbCBkbyB0aGlzIG9uZSBtb3JlLi4uCi0gUTogQ29uc2lkZXIgUmVnRXggcGF0dGVybjogYCI8PC4qPj4iYAogICAgLSBXaGF0J3MgaXQgZG8/CiAgICAtIERvIHlvdSB0aGluayB0aGlzIHdpbGwgd29yaz8KICAgIC0gRG8gd2UgbmVlZCBhbiBlc2NhcGUgZm9yIHRoZSBzcGVjaWFsIGNoYXJhY3RlcnM/IGBcXGAKCmBgYHtyfQpqdW5rIDwtIGdyZXBsKHBhdHRlcm4gPSAiPDwuKj4+IiwgeCA9IEFsbE1hY2JldGgkbGluZXMpCgojIEJlZm9yZQpBbGxNYWNiZXRoICU+JQogIGZpbHRlcihqdW5rKSAlPiUKICBzZWxlY3Qoc3BlYWtlcnMsIGxpbmVzKQoKIyBDb3JyZWN0aW9uCkFsbE1hY2JldGggPC0gCiAgQWxsTWFjYmV0aCAlPiUKICBtdXRhdGUobGluZXMgPSBnc3ViKHBhdHRlcm4gPSAiPDwuKj4+IiwgcmVwbGFjZW1lbnQgPSAiIiwgeCA9IGxpbmVzKSkKCgojIEFmdGVyCkFsbE1hY2JldGggJT4lCiAgZmlsdGVyKGp1bmspICU+JQogIHNlbGVjdChzcGVha2VycywgbGluZXMpCmBgYAoKPCEtLSBEYXkzIC0tPgoKIyBUaW1lIHRvIHRha2UgYSBsb29rIQoKLSBROiBIb3cgbWlnaHQgd2UgdXNlIG91ciByZXN1bHQgc28gZmFyIHRvIGNvdW50IGhvdyBtYW55IHdvcmRzIGFyZSBhdHRyaWJ1dGVkIHRvIGVhY2ggY2hhcmFjdGVyPwoKCiMgRmlyc3QgYXR0ZW1wdDogQ291bnRpbmcgd29yZHMKCi0gUTogSG93J3MgaXQgbG9vaz8gCj4tIFE6IEhvdyB3b3VsZCB5b3UgZGVidWcgdGhpcz8KCmBgYHtyfQpjb3VudFdvcmRzIDwtIGZ1bmN0aW9uKGxpbmUpIHsKICByZXR1cm4obGVuZ3RoKHN0cnNwbGl0KHggPSBsaW5lLCBzcGxpdCA9ICJcXHMrIikpKQp9CgpBbGxNYWNiZXRoJG5Xb3JkcyA8LSBzYXBwbHkoWCA9IEFsbE1hY2JldGgkbGluZXMsIEZVTiA9IGNvdW50V29yZHMpCgpoZWFkKEFsbE1hY2JldGgpCgpgYGAKCiMgRGVidWdnaW5nIG91ciBjbGVhbnVwCgotIFE6IEhvdydzIGl0IGxvb2s/Cj4tIFE6IFdoYXQgdHlwZSBvZiBvYmplY3QgaXMgdGhpcz8KCmBgYHtyfQpzdHJzcGxpdCh4ID0gQWxsTWFjYmV0aCRsaW5lc1sxXSwgc3BsaXQgPSAiXFxzKyIpCgpgYGAKCgojIERlYnVnZ2luZyBvdXIgY2xlYW51cAoKLSBROiBIb3cncyBpdCBsb29rPwoKYGBge3J9CmNvdW50V29yZHMgPC0gZnVuY3Rpb24obGluZSkgewogIHJldHVybihsZW5ndGgoc3Ryc3BsaXQoeCA9IGxpbmUsIHNwbGl0ID0gIlxccysiKVtbMV1dKSkKfQoKQWxsTWFjYmV0aCRuV29yZHMgPC0gc2FwcGx5KFggPSBBbGxNYWNiZXRoJGxpbmVzLCBGVU4gPSBjb3VudFdvcmRzKQoKaGVhZChBbGxNYWNiZXRoKQpgYGAKCgoKIyBDaG9vc2luZyB5b3VyIGNoYXJhY3RlcgoKLSBSZWNhbGw6IFlvdSB3YW50IHRvIAogICAgLSBpbXByZXNzIHNvbWVvbmUgaW4gVEhFQSAxMDIKICAgIC0gY2hvb3NlIGFuICJpbXBvcnRhbnQiIGNoYXJhY3RlcgogICAgLSBub3QgdG9vIG11Y2ggdG8gbWVtb3JpemUKLSBROiBXaGF0IGFyZSB5b3VyIHByaW9yaXRpZXM/CiAgICAtIE1vc3QgbGluZXM/CiAgICAtIExvbmdlc3QgbGluZXM/CiAgICAtIFdobyBoYXMgdGhlIG1vc3QgdG90YWwgY29udGVudCB0byBtZW1vcml6ZT8KICAgIC0gU29tZXRoaW5nIGVsc2U/Ci0gUTogSG93IHNob3VsZCB3ZSBmaWd1cmUgaXQgb3V0IGZyb20gaGVyZT8KCmBgYHtyfQojIExvbmdlc3QgaW5kaXZpZHVhbCBsaW5lcyAKQWxsTWFjYmV0aCAlPiUKICBzZWxlY3Qoc3BlYWtlcnMsIG5Xb3JkcywgbGluZXMpICU+JQogIGFycmFuZ2UoZGVzYyhuV29yZHMpKSAKCmBgYAoKIyBDaGFyYWN0ZXIgYW5hbHlzaXMKCi0gTW9zdCBsaW5lcz8KLSBMb25nZXN0IGxpbmVzPwotIFdobyBoYXMgdGhlIG1vc3QgdG90YWwgY29udGVudCB0byBtZW1vcml6ZT8KLSBTb21ldGhpbmcgZWxzZT8KCmBgYHtyfQpNYWNiZXRoU3VtbWFyeSA8LSAKICBBbGxNYWNiZXRoICU+JSAKICBncm91cF9ieShzcGVha2VycykgJT4lCiAgc3VtbWFyaXNlKGxpbmVzID0gbigpLCAKICAgICAgICAgICAgdG90YWxXb3JkcyA9IHN1bShuV29yZHMpLCAKICAgICAgICAgICAgd29yZHNQZXJMaW5lID0gdG90YWxXb3Jkcy9saW5lcykKCgojIE1vc3Qgd29yZHMgcGVyIGxpbmUKTWFjYmV0aFN1bW1hcnkgJT4lCiAgYXJyYW5nZShkZXNjKHdvcmRzUGVyTGluZSkpICU+JQogIGhlYWQoMTApCgoKIyBNb3N0IHRvdGFsIGNvbnRlbnQKTWFjYmV0aFN1bW1hcnkgJT4lCiAgYXJyYW5nZShkZXNjKHRvdGFsV29yZHMpKSAlPiUKICBoZWFkKDEwKQoKYGBgCgoKIyBXaG8gZG8geW91IGNob29zZT8KCi0gTGV0J3MgZ2V0IHlvdXIgbGluZXMhCgpgYGB7cn0KTXlMaW5lcyA8LSAKICBBbGxNYWNiZXRoICU+JSAKICBmaWx0ZXIoZ3JlcGwoIiAgTEFEWSBNQUNCRVRIXFwuIiwgc3BlYWtlcnMpKQoKaGVhZChNeUxpbmVzKQpgYGAKCgoKCiMgVGV4dCBNaW5pbmcgTWFjYmV0aAoKLSBJIHdhbnQgdG8gZGlnIGEgbGl0dGxlIGRlZXBlci4uLiAKLSAodHJhbnNsYXRpb246IEkgZm91bmQgbW9yZSAyIG1pbiBNYWNiZXRoIHZpZGVvcyEpCi0gKipROiBXaGF0IHdvdWxkIGJlIHNvbWUgaW50ZXJlc3RpbmcgcXVlc3Rpb25zIHRvIGV4cGxvcmUgd2hlbiB0ZXh0IG1pbmluZyBNYWNiZXRoPyoqCgoKIyBUZXh0IE1pbmluZyBNYWNiZXRoCgotIFNoYWtlc3BlYXJlYW4gcGxheXMgYXJlIGJlbGlldmVkIHRvIHNoYXJlIGEgY29tbW9uIGxpdGVyYXJ5IHN0cnVjdHVyZQotIEVhY2ggQUNUIGluIHRoZSBwbGF5IGhhcyBhIGRpZmZlcmVudCwgYnV0IGltcG9ydGFudCByb2xlIGluIHRoZSBkcmFtYQotICoqUHJvYmxlbSoqIHBlb3BsZSB0YWxrIHdlaXJkIGluIE1hY2JldGggKEknbSBraWRkaW5nLi4uIHNvcnQgb2YpCi0gTGV0J3MgZG8gc29tZSB0ZXh0IGFuYWx5c2lzIG9mIGEgIm1vZGVybiIgdHJhbnNsYXRpb24gb2YgdGhlIHBsYXkKLSA8aHR0cDovL3d3dy5ub3N3ZWF0c2hha2VzcGVhcmUuY29tL3NoYWtlc3BlYXJlcy1wbGF5cy9tb2Rlcm4tbWFjYmV0aC8+Ci0gUTogSG93IHdvdWxkIHlvdSBzb2x2ZSB0aGlzIHNvIHdlIGNvdWxkIHByb2Nlc3MgdGhlIHRleHQ/CgoKIyBTaWRlYmFyICJUaGUgR2VuZXJhbCBQcm9ibGVtIgoKLSBJIHB1bnRlZC4gIAotIEkgd2FzIGJ1c3kgd2l0aCBEYXRhRmVzdCwgc28gSSB0b29rIHRoZSBzbG9wcHkgd2F5IG91dAotIEkgbWFkZSBhIGRpcmVjdG9yeSB3aXRoIGV2ZXJ5IHNjZW5lIGluIHRoZSBwbGF5IGFzIGEgdGV4dCBmaWxlCiAgICAtIEkgdGhvdWdodCBhYm91dCBzb2x2aW5nIHRoZSAiZ2VuZXJhbCBwcm9ibGVtIgogICAgLSBJIGRlY2lkZWQgdG8gInBhc3MgdGhlIHNhbHQiIGFuZCBzaG93IGEgZmF2b3JpdGUgY29taWMgYWJvdXQgaXQgaW5zdGVhZAotIFE6IFdoeSBhbSBJIGNsYWltaW5nIHRoYXQgbXkgYXBwcm9hY2ggaXMgInNsb3BweSI/Ci0gUTogV2hhdCB3b3VsZCBiZSBhIG1vcmUgZWxlZ2FudCBzb2x1dGlvbj8gCgohW2h0dHBzOi8vbS54a2NkLmNvbS85NzQvXShnZW5lcmFsUHJvYmxlbV94a2NkOTU0LnBuZykKCgojIFRleHQgQW5hbHlzaXMKCi0gTm93LCBJJ3ZlIGdvdCBhIHdob2xlIGRpcmVjdG9yeSBmdWxsIG9mIHRleHQgZmlsZXMuCi0gSSB3YXMgYXQgbGVhc3Qgc21hcnQgZW5vdWdoIHRvIG5hbWUgdGhlbSBpbiBhIHN0cnVjdHVyZWQgd2F5OiAKICAgIC0gZGVkaWNhdGVkIGRpcmVjdG9yeSBsb2NhdGlvbgogICAgLSB1c2VmdWwgbmFtaW5nIGNvbnZlbnRpb246ICJhY3QxX3NjZW5lMS50eHQiCi0gYHJlYWR0ZXh0YCBjYW4gcmVhZCBhbGwgdGhlICIqLnR4dCIgZmlsZXMgaW4gdGhlIHJlcXVlc3RlZCBkaXJlY3RvcnkKICAgIC0gYGRvY3ZhcnNmcm9tYCBpbmRpY2F0ZXMgdGhhdCB0aGUgZmlsZSBuYW1lIGhhcyBzb21lIHZhcmlhYmxlcyBJIHdhbnQKICAgIC0gYGR2c2VwYCBpbmRpY2F0ZXMgdGhhdCB0aGV5IGFyZSBzZXBhcmF0ZWQgYnkgYW4gdW5kZXJzY29yZQotIHJlc3VsdCBpcyBhIGRhdGEgZnJhbWUgCgoKYGBge3J9CnJlcXVpcmUocmVhZHRleHQpCgpNb2Rlcm5NYWNiZXRoIDwtIAogIHJlYWR0ZXh0OjpyZWFkdGV4dChmaWxlID0gIi9Vc2Vycy9tYXR0YmVja21hbi9Eb2N1bWVudHMvR2l0SHViL1RlYWNoaW5nL1NUQVQtMzgwLzIwMTkgU3ByaW5nL0NsYXNzTm90ZXMvMTUtbWRzci9wbGFpbl9NYWNiZXRoLyoudHh0IiwgZG9jdmFyc2Zyb20gPSAiZmlsZW5hbWVzIiwgZHZzZXAgPSAiXyIpICU+JQogIGFzLnRpYmJsZSgpCgpoZWFkKE1vZGVybk1hY2JldGgpCgpgYGAKCjwhLS0gRGF5NCAtLT4KCiMgQ29ycHVzCgotIHdlJ3JlIG9mdGVuIGludGVyZXN0ZWQgaW4gKm1hbnkgZG9jdW1lbnRzKiB3aGVuIHRleHQgbWluaW5nIAotIGEgKipjb3JwdXMqKiBpcyBhIGNvbGxlY3Rpb24gb2YgbWFueSBkb2N1bWVudHMKLSBROiBJbiBvdXIgZXhhbXBsZTogCiAgICAtIHdoYXQgaXMgdGhlIGNvcnB1cz8KICAgIC0gd2hhdCBhcmUgdGhlIGRvY3VtZW50cz8KLSBROiBEZXNjcmliZSBhICJjb3JwdXMiIGFuZCB0aGUgYXNzb2NpYXRlZCAiZG9jdW1lbnRzIiBmb3IKICAgIC0gYSBkaWZmZXJlbnQgYW5hbHlzaXMgb2YgU2hha2VzcGVhcmU/CiAgICAtIHNvbWUgb3RoZXIgYXV0aG9yPwogICAgLSBvdGhlciBleGFtcGxlcz8gCgoKIyBNb2Rlcm4gTWFjYmV0aCBDb3JwdXMKCi0gYmVmb3JlIHdlIGNhbiByZWFsbHkgZGlnIGludG8gb3VyIHN0dWR5IG9mIHRoZSBkaWZmZXJlbmNlcyBhbW9uZyBhY3RzIGluIE1hY2JldGgsIHdlIG5lZWQgdG8gYnVpbGQgdXAgYSBjb3VwbGUgdGV4dCBhbmFseXNpcyB0b29scwotIHdlJ2xsIHVzZSB0aGUgZnVsbCB0ZXh0IG9mIE1vZGVybiBNYWNiZXRoIHRvIGdldCBzdGFydGVkCgoKCiMgVGlkeSBUZXh0IEZvcm1hdAoKLSBSZWNhbGw6IHRpZHkgZGF0YSBoYXMgYSBzcGVjaWZpYyBzdHJ1Y3R1cmUKICAgIC0gZWFjaCBjYXNlIGlzIGEgcm93CiAgICAtIGVhY2ggdmFyaWFibGUgaXMgYSBjb2x1bW4KLSAqKlRpZHkgdGV4dCoqIGNvdWxkIHNpbWlsYXJseSBiZSBkZXNjcmliZWQgYXMgYSB0YWJsZSB3aXRoICoqb25lIHRva2VuLXBlci1yb3cqKgotIGEgKip0b2tlbioqIGlzIGEgbWVhbmluZ2Z1bCB1bml0IG9mIHRleHQgdXNlZnVsIGZvciBhbmFseXNpcwogICAgLSB3b3JkICh2ZXJ5IGNvbW1vbikKICAgIC0gbi1ncmFtIChzZXF1ZW5jZSBvZiB3b3JkcykKICAgIC0gc2VudGVuY2UKICAgIC0gcGFyYWdyYXBoCi0gKip0b2tlbml6YXRpb24qKiBpcyB0aGUgcHJvY2VzcyBvZiBzcGxpdHRpbmcgdGV4dCBpbnRvIHRva2VucwoKIyBUb2tlbml6YXRpb24gc3RlcAoKLSBsZXQncyB0b2tlbml6ZSBNb2Rlcm4gTWFjYmV0aAotIHdlJ2xsIHVzZSBzb21lIHRvb2xzIGZyb20gdGhlIGB0aWR5dGV4dGAgcGFja2FnZQotIGB1bm5lc3RfdG9rZW5zYCBicmVha3MgdGhlIHRleHQgaW50byBpbmRpdmlkdWFsIHRva2Vucy4uLiBhdXRvbWF0aWNhbGx5CiAgICAtIGl0IGtlZXBzIHRoZSBvdGhlciBjb2x1bW5zLS1hY3QgJiBzY2VuZSBoZXJlCiAgICAtIHN0cmlwcyBwdW5jdHVhdGlvbgogICAgLSBjb252ZXJ0cyB0byBsb3dlcmNhc2UgKGRlZmF1bHQgYXJnOiBgdG9fbG93ZXIgPSBUUlVFYCkKCgpgYGB7cn0KcmVxdWlyZSh0aWR5dGV4dCkKCk1vZGVybk1hY2JldGggJT4lCiAgc2VsZWN0KHRleHQsIGFjdCA9IGRvY3ZhcjEsIHNjZW5lID0gZG9jdmFyMikgJT4lCiAgdW5uZXN0X3Rva2VucyhvdXRwdXQgPSB3b3JkLCBpbnB1dCA9IHRleHQpICAgICAgICAgIyBzaW5nbGUgd29yZCB0b2tlbml6YXRpb24KICAjIHVubmVzdF90b2tlbnMob3V0cHV0ID0gd29yZCwgaW5wdXQgPSB0ZXh0LCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMykgICAjIG4tZ3JhbSB0b2tlbml6YXRpb24KYGBgCgojIEJhZyBvZiB3b3JkcwoKLSBXZSdyZSB1c2luZyBhIHNpbXBsZSBhcHByb2FjaCB0byB0ZXh0IGFuYWx5c2lzIGNhbGxlZCBiYWcgb2Ygd29yZHMKLSAqKkJhZyBvZiB3b3JkcyoqIHByZXNlcnZlcyB0ZXJtIGZyZXF1ZW5jaWVzLCBidXQgZGlzcmVnYXJkcyB3b3JkIG9yZGVyLCBncmFtbWFyLCBldGMKCmBgYHtyfQpNb2Rlcm5NYWNiZXRoX3RpZHkgPC0gCiAgTW9kZXJuTWFjYmV0aCAlPiUKICBzZWxlY3QodGV4dCwgYWN0ID0gZG9jdmFyMSwgc2NlbmUgPSBkb2N2YXIyKSAlPiUKICB1bm5lc3RfdG9rZW5zKG91dHB1dCA9IHdvcmQsIGlucHV0ID0gdGV4dCkgICAgICAgICAjIHNpbmdsZSB3b3JkIHRva2VuaXphdGlvbgpgYGAKCgojIFRva2VuIGZyZXF1ZW5jeQoKLSBROiBXaGF0IGhhdmUgd2UgbGVhcm5lZCBhYm91dCBBY3QgMT8KLSBROiBXaGF0IHNob3VsZCB3ZSBkbyBuZXh0PwoKYGBge3J9Ck1vZGVybk1hY2JldGhfdGlkeSAlPiUKICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkKYGBgCgoKIyBTdG9wIHdvcmRzCgotICoqc3RvcCB3b3JkcyoqIGFyZSB3b3JkcyB0aGF0IGFyZSBub3QgdXNlZnVsIGZvciBhbiBhbmFseXNpcwogICAgLSBleHRyZW1lbHkgY29tbW9uIHdvcmRzIGxpa2UgInRoZSIsICJhbmQiLCAidG8iLCBhcyBzaG93bgogICAgLSB3b3JkcyB0aGF0IGFyZW4ndCB1c2VmdWwgaW4gYW5hbHlzaXMgbGlrZSAiYWNjb3JkaW5nbHkiCi0gVGhlIGNvbnRlbnRzIG9mIHRoZSBzdG9wIHdvcmQgbGlzdCBtYXR0ZXJzIGEgbG90CiAgICAtIGB0aWR5dGV4dGAgcGFja2FnZSBpbmNsdWRlcyBhIGRhdGEgc2V0IGNhbGxlZCBgc3RvcF93b3Jkc2AgdG8gZ2V0IHlvdSBzdGFydGVkCiAgICAtIFRoZSBzdG9wIHdvcmRzIGFyZSBkaWZmZXJlbnQgZm9yIGVhY2ggbGFuZ3VhZ2UKICAgIC0gWW91IG1pZ2h0IGFkZC9tb2RpZnkgdGhlIHN0b3Agd29yZHMgYmFzZWQgb24gc3BlY2lhbGl6ZWQgZXhwZXJ0aXNlIHdpdGggdGhlIGNvbnRleHQgCi0gUTogV2hhdCBpZiB3ZSBoYWQgdXNlZCB0aGUgb3JpZ2luYWwgU2hha2VzcGVhcmU/CgpgYGB7cn0KIyBuYXRpdmUgdGV4dApNb2Rlcm5NYWNiZXRoX3RpZHkgJT4lCiAgY291bnQod29yZCwgc29ydCA9IFRSVUUpCgojIGxvYWQgc3RvcCB3b3JkIGxpc3QgKEVuZ2xpc2gpCmRhdGEoInN0b3Bfd29yZHMiKSAgICMgZnJvbSBgdGlkeXRleHRgIHBhY2thZ2UKCmhlYWQoc3RvcF93b3JkcykKdGFpbChzdG9wX3dvcmRzKQpgYGAKCiMgUmVtb3Zpbmcgc3RvcCB3b3JkcwoKLSBmaWx0ZXIgb3V0IHN0b3Agd29yZHMKLSBlcXVpdmFsZW50bHksIHdlIGNvdWxkIHVzZSBhbiBgYW50aV9qb2luYCBoZXJlCi0gaXQgdHVybnMgb3V0IE1hY2JldGggaXMgYSBiaWcgZGVhbCBhbmQgaGUncyBhbGwgb3ZlciB0aGUgcGxheSAKICAgIC0gd2UgbWlnaHQgY29uc2lkZXIgcmVtb3ZpbmcgIm1hY2JldGgiIGZyb20gdGhlIGJhZyBvZiB3b3JkcwogICAgLSBROiBob3cgYWJvdXQgYW5vdGhlciBzdWdnZXN0aW9uPwogICAgICAgIC0gLi4ubWF5YmUgc29tZXRoaW5nIHRoYXQgc2VlbXMgbGlrZSBpdCBjbGVhcmx5IHNob3VsZCBoYXZlIGJlZW4gcmVtb3ZlZCBhbHJlYWR5Pz8KICAgICAgICAtIGlzIG91ciBsaXN0IG9mIHN0b3Agd29yZHMgcmVhbGx5IHRoYXQgYmFkPz8KCgpgYGB7cn0KIyAjIGBzdG9wX3dvcmRzYCBoYXMgdHdvIGNvbHVtbnM6ICJ3b3JkIiBhbmQgImxleGljb24KIyBzdG9wX3dvcmRzIDwtCiMgICByYmluZChzdG9wX3dvcmRzLCAgICAgICAgICAgCiMgICAgICAgICBjKCJtYWNiZXRoIiwgImN1c3RvbSIpKQojIAojIHN0b3Bfd29yZHMgJT4lCiMgICBmaWx0ZXIod29yZCA9PSAiaXQncyIpCiMgCiMgIyB3ZSdsbCBuZWVkIHRvIHVzZSBSZWdFeCB0byBjbGVhbiB0aGVzZSB1cAojIGdyZXAocGF0dGVybiA9ICJpdC5zIiwgeCA9IE1vZGVybk1hY2JldGhfdGlkeSR3b3JkLCB2YWx1ZSA9IFRSVUUpCiMgZ3JlcChwYXR0ZXJuID0gIuKAmSIsIHggPSBNb2Rlcm5NYWNiZXRoX3RpZHkkd29yZCwgdmFsdWUgPSBUUlVFKSAlPiUgaGVhZCgzMCkKCgpNb2Rlcm5NYWNiZXRoX3RpZHkgPC0gCiAgTW9kZXJuTWFjYmV0aF90aWR5ICU+JQogICMgbXV0YXRlKHdvcmQgPSBnc3ViKHBhdHRlcm4gPSAsIHJlcGxhY2VtZW50ID0gLCB4ID0gd29yZCkpICU+JQogIGZpbHRlcighKHdvcmQgJWluJSBzdG9wX3dvcmRzJHdvcmQpKQoKTW9kZXJuTWFjYmV0aF90aWR5ICU+JQogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKQpgYGAKCgojIFRpZHkgdGV4dCBwaXBlcyB0byBgZ2dwbG90MmAKCi0gTGV0J3MgbWFrZSBhIGhpc3RvZ3JhbSBvZiB3b3JkIGZyZXF1ZW5jaWVzCi0gUTogV2hhdCdzIGdvaW5nIG9uIGluIEFjdCAxIG9mIE1hY2JldGg/CgoKYGBge3J9Ck1vZGVybk1hY2JldGhfdGlkeSAlPiUKICBtdXRhdGUod29yZCA9IGdzdWIocGF0dGVybiA9ICLigJkiLCByZXBsYWNlbWVudCA9ICInIiwgeCA9IHdvcmQpKSAlPiUKICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkgJT4lCiAgZmlsdGVyKG4gPiAyMCkgJT4lCiAgbXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIG4pKSAlPiUKICBnZ3Bsb3QoYWVzKHdvcmQsIG4pKSArCiAgZ2VvbV9jb2woKSArCiAgeGxhYihOVUxMKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKCgojIFdvcmQgY2xvdWRzCgotICoqd29yZCBjbG91ZHMqKiBhcmUgYW5vdGhlciBwb3B1bGFyIHZpc3VhbGl6YXRpb24gb2YgdG9rZW4gZnJlcXVlbmN5IChhbmFsb2dvdXMgdG8gb3VyIGhpc3RvZ3JhbSkKLSB0aGUgYHdvcmRjbG91ZGAgcGFja2FnZSB1c2VzIGJhc2UgUiBncmFwaGljcywgYnV0IHRoZSBgd2l0aCggKWAgZnVuY3Rpb24gaGVscHMKLSBROiBXaGF0J3MgTWFjYmV0aCBhYm91dD8KICAgIC0gV2hhdCBoYXZlIHdlIGxlYXJuZWQgZnJvbSB0aGlzPwogICAgLSBXaGF0IHNob3VsZCB3ZSBkbyBuZXh0PwoKYGBge3J9Ck1vZGVybk1hY2JldGhfdGlkeSAlPiUKICBmaWx0ZXIoISh3b3JkICVpbiUgc3RvcF93b3JkcyR3b3JkKSkgJT4lCiAgY291bnQod29yZCkgJT4lCiAgd2l0aCguLCB3b3JkY2xvdWQod29yZCwgbiwgbWF4LndvcmRzID0gNDUpKQoKYGBgCgoKCiMgd29yZCBjbG91ZHM6IE1vZGVybiB2cyBvcmlnaW5hbAoKLSBJJ3ZlIG1hZGUgYSBoZWxwZXIgZnVuY3Rpb24gdG8gcHJlcCB0aGUgY29ycHVzIAotIEkgYWxzbyBhZGRlZCAic2FpZCIgdG8gb3VyIHN0b3Agd29yZCBsaXN0CiAgICAtIGBjKHN0b3B3b3JkcygiZW5nbGlzaCIpLCAic2FpZCIpYAogICAgLSBzaG91bGQgIm1hY2JldGgiIGJlIGFkZGVkIHRvIHRoZSBsaXN0PwoKYGBge3J9CkFsbE1hY2JldGhfdGlkeSA8LSAKICBBbGxNYWNiZXRoICU+JQogIHNlbGVjdChzcGVha2VycywgbGluZXMpICU+JQogIHVubmVzdF90b2tlbnMob3V0cHV0ID0gd29yZCwgaW5wdXQgPSBsaW5lcykgICAgICMgc2luZ2xlIHdvcmQgdG9rZW5pemF0aW9uCgpoZWFkKEFsbE1hY2JldGhfdGlkeSkKYGBgCgoKYGBge3J9CiMgaGVscGVyIGZ1bmN0aW9uIGZvciBtYWtpbmcgd29yZGNsb3VkcwptYWNiZXRoX3dvcmRjbG91ZCA8LSBmdW5jdGlvbihDb3JwdXNfdGlkeSwgbWF4V29yZHMgPSA0NSkgewogIENvcnB1c190aWR5ICU+JQogICAgZmlsdGVyKCEod29yZCAlaW4lIHN0b3Bfd29yZHMkd29yZCkpICU+JQogICAgY291bnQod29yZCkgJT4lCiAgICB3aXRoKC4sIHdvcmRjbG91ZCh3b3JkLCBuLCBtYXgud29yZHMgPSBtYXhXb3JkcykpCgp9CgptYWNiZXRoX3dvcmRjbG91ZChDb3JwdXMgPSBBbGxNYWNiZXRoX3RpZHkpCm1hY2JldGhfd29yZGNsb3VkKENvcnB1cyA9IE1vZGVybk1hY2JldGhfdGlkeSkKCgpgYGAKCgojIEFjdCBieSBBY3Qgd29yZCBjbG91ZHMKCi0gUTogQ2FuIHdlIHRlbGwgYW55dGhpbmcgYWJvdXQgdGhlIHN0b3J5bGluZT8KLSBROiBIb3cgcmVsaWFibGUgYXJlIHdvcmQgY2xvdWRzIGFibGUgdG8gY29udmV5IG1lYW5pbmcgb2YgZWFjaCBBY3Q/CgpgYGB7cn0KCm1hY2JldGhfd29yZGNsb3VkKGZpbHRlcihNb2Rlcm5NYWNiZXRoX3RpZHksIGFjdCA9PSAiQWN0MSIpKQptYWNiZXRoX3dvcmRjbG91ZChmaWx0ZXIoTW9kZXJuTWFjYmV0aF90aWR5LCBhY3QgPT0gIkFjdDIiKSkKbWFjYmV0aF93b3JkY2xvdWQoZmlsdGVyKE1vZGVybk1hY2JldGhfdGlkeSwgYWN0ID09ICJBY3QzIikpCm1hY2JldGhfd29yZGNsb3VkKGZpbHRlcihNb2Rlcm5NYWNiZXRoX3RpZHksIGFjdCA9PSAiQWN0NCIpKQptYWNiZXRoX3dvcmRjbG91ZChmaWx0ZXIoTW9kZXJuTWFjYmV0aF90aWR5LCBhY3QgPT0gIkFjdDUiKSkKCgpgYGAKCjwhLS0gRGF5NSAtLT4KCiMgUmVjYWxsIAoKLSBIdW1hbnM6IAogICAgLSBleGNlbGxlbnQgYXQgKnVuZGVyc3RhbmRpbmcqIHRleHQKICAgIC0gbm90IGFzIGdvb2QgYXQgKnN0b3JpbmcqIHRleHQKLSBDb21wdXRlcnM6IAogICAgLSBleGNlbGxlbnQgYXQgKnN0b3JpbmcqIHRleHQKICAgIC0gbm90IGFzIGdvb2QgYXQgKnVuZGVyc3RhbmRpbmcqIHRleHQgCi0gSHVtYW4gKyBDb21wdXRlcj8/CiAgICAtIGV4Y2VsbGVudCBhdCB1bmRlcnN0YW5kaW5nICYgc3RvcmluZyB0ZXh0PwogICAgLSBub3Qgc28gZ29vZCBhdCBzdG9yaW5nICYgdW5kZXJzdGFuZGluZyB0ZXh0PwogICAgLSAoZGVwZW5kcyBvbiB0aGUgaHVtYW4gJiB0aGUgY29tcHV0ZXIuLi4/KQoKIyBTZW50aW1lbnQKCi0gdG9rZW4gZnJlcXVlbmNpZXMgYXJlIE9LLCBidXQgd29yZHMgZXZva2UgZW1vdGlvbgotIGh1bWFucyBhcmUgKHVzdWFsbHkpIGV4Y2VsbGVudCBhdCB1bmRlcnN0YW5kaW5nIGVtb3Rpb25zIAotIHdvcmQgY2hvaWNlIGNhbiBpbmRpY2F0ZSAKICAgIC0gZ2VuZXJhbCBwb3NpdGl2ZS9uZWdhdGl2ZSBlbW90aW9uCiAgICAtIHNwZWNpZmljIGVtb3Rpb25zIGxpa2Ugc3VycHJpc2Ugb3IgZGlzZ3VzdAotIGdvb2QgbmV3cyEKICAgIC0gcGVvcGxlIGhhdmUgY29tcGlsZWQgZGV0YWlsZWQgd29yZCBsaXN0cyBmb3IgdGhpcwogICAgLSBgdGlkeXRleHRgIHBhY2thZ2UgaW5jbHVkZXMgc2V2ZXJhbCBpbiB0aGUgYHNlbnRpbWVudHNgIGRhdGEgc2V0CgpgYGB7cn0KcmVxdWlyZSh0aWR5dGV4dCkKZGF0YSgic2VudGltZW50cyIpCgpoZWFkKHNlbnRpbWVudHMsIDEwKQp0YWlsKHNlbnRpbWVudHMsIDEwKQpgYGAKCgojIFNlbnRpbWVudCBsZXhpY29ucwoKLSBgc2VudGltZW50c2AgY29udGFpbnMgdGhyZWUgZ2VuZXJhbCBwdXJwb3NlIGxleGljb25zCiAgICAtIEFGSU5OLS1yYXRpbmcgZnJvbSAtNSAodmVyeSBuZWdhdGl2ZSkgdG8gKzUgKHZlcnkgcG9zaXRpdmUpCiAgICAtIGJpbmctLXBvc2l0aXZlL25lZ2F0aXZlIGNsYXNzaWZpY2F0aW9uCiAgICAtIG5yYy0tY2xhc3NpZmljYXRpb24gaW50byBjYXRlZ29yaWVzCiAgICAgICAgLSBwb3NpdGl2ZQogICAgICAgIC0gbmVnYXRpdmUKICAgICAgICAtIGZlYXIKICAgICAgICAtIHNhZG5lc3MKLSBgdGlkeXRleHRgIHByb3ZpZGVzIGZ1bmN0aW9uIGBnZXRfc2VudGltZW50cyggKWAgdG8gY2hvb3NlIGEgbGV4aWNvbgotIGxvdHMgb2Ygd29yZHMgYXJlIHF1aXRlIG5ldXRyYWwsIHNvIHRoZXkgY2FuIGJlIGV4Y2x1ZGVkIGZyb20gc2VudGltZW50IGxleGljb25zCi0gdGhlIHNlbnRpbWVudHMgaW4gdGhlc2UgbGV4aWNvbnMgbWlnaHQgYmUgY29uc3RydWN0ZWQgYnkgY3Jvd2Rzb3VyY2luZyBvciByZXNlYXJjaCwgdGhlbiB2YWxpZGF0ZWQgdXNpbmcgcmVzdGF1cmFudCwgbW92aWUsIG9yIGFtYXpvbiByZXZpZXdzCi0gUTogd2h5IG1pZ2h0IHdlIGhlc2l0YXRlIHRvIGFwcGx5IHRoZXNlIHNlbnRpbWVudCBsZXhpY29ucyB0byBTaGFrZXNwZWFyZSdzIGxpdGVyYXR1cmU/Ci0gUTogQW55IG90aGVyIGNvbmNlcm5zIHRoYXQgbWlnaHQgbWFrZSBpdCBoYXJkIHRvIGNhcHR1cmUgc2VudGltZW50PwogICAgLSBvbmUtd29yZCB0b2tlbnMKICAgIC0gZXh0cmVtZWx5IGxhcmdlIGNodW5rcyBvZiB0ZXh0IChhbGwgb2YgTWFjYmV0aCkKCmBgYHtyfQpnZXRfc2VudGltZW50cygibnJjIikgJT4lCiAgZ3JvdXBfYnkoc2VudGltZW50KSAlPiUKICBzdW1tYXJpc2UoTiA9IG4oKSkgJT4lCiAgYXJyYW5nZShkZXNjKE4pKQpgYGAKCgoKIyBTZW50aW1lbnQgYW5hbHlzaXMgb2YgTWFjYmV0aAoKLSBzaW5jZSB3ZSdyZSB3b3JraW5nIHdpdGggdGlkeSBkYXRhLCB3ZSBjYW4gdXNlIGFuIGBpbm5lcl9qb2luYCBmb3Igc2VudGltZW50IGFuYWx5c2lzCi0gdGhpcyBjb252ZXJ0cyBzZXZlcmFsICJ0ZXh0IG1pbmluZyIgdGFza3MgaW50byBzaW1wbGUgdGlkeSBkYXRhIGFuYWx5c2lzIHRhc2tzCi0gbGV0J3MgaW52ZXN0aWdhdGUgc29tZSBjb21tb24gdGVybXMgaW4gTWFjYmV0aCB0aGF0IHN1Z2dlc3QgImFudGljaXBhdGlvbiIKCmBgYHtyfQpucmNfYW50aWNpcGF0aW9uIDwtIGdldF9zZW50aW1lbnRzKCJucmMiKSAlPiUKICBmaWx0ZXIoc2VudGltZW50ID09ICJhbnRpY2lwYXRpb24iKQoKTW9kZXJuTWFjYmV0aF90aWR5ICU+JQogIGlubmVyX2pvaW4obnJjX2FudGljaXBhdGlvbikgJT4lCiAgY291bnQod29yZCwgc29ydCA9IFRSVUUpCiAgCmBgYAoKIyBDaGFuZ2VzIGluIFNlbnRpbWVudAoKLSBzbWFsbCBzZWN0aW9ucyBvZiB0ZXh0IG1heSBub3QgaGF2ZSBlbm91Z2ggd29yZHMgdG8gY29tbXVuaWNhdGUgc2VudGltZW50Ci0gZXh0cmVtZWx5IGxhcmdlIHNlY3Rpb25zIG1pZ2h0IGF2ZXJhZ2Ugb3V0IHRoZSBzZW50aW1lbnQgd2Ugd2FudCB0byBjYXB0dXJlCi0gZm9yIFNoYWtlc3BlYXJlJ3Mgb3JpZ2luYWwgdGV4dCwgd2UgbWlnaHQgY2hvb3NlIGJsb2NrcyBvZiA4MCBsaW5lcyBvciBzbwotIGZvciBNb2Rlcm4gTWFjYmV0aCwgd2UnbGwgY2hvb3NlIHdob2xlIHNjZW5lcyAocGVyaGFwcyBhIGJpdCBsYXJnZS92YXJpYWJsZSkKLSB3ZSdsbCBjYWxjdWxhdGUgYSBtZXRyaWMgdG8gYXNzZXNzIGVhY2ggc2NlbmUKICAgIC0gZnJvbSAtMTAwIHRvIDEwMCAKICAgIC0gMTAwIG1lYW5zIDEwMCUgcG9zaXRpdmUgc2VudGltZW50CgpgYGB7cn0KTW9kZXJuTWFjYmV0aF9zZW50aW1lbnQgPC0gCiAgTW9kZXJuTWFjYmV0aF90aWR5ICU+JQogIG11dGF0ZShhY3Rfc2NlbmUgPSBwYXN0ZShhY3QsIHNjZW5lLCBzZXAgPSAiXyIpKSAlPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJhZmlubiIpKSAlPiUKICBncm91cF9ieShhY3QsIHNjZW5lKSAlPiUKICBzdW1tYXJpc2Uoc2VudGltZW50ID0gbWVhbihzY29yZSwgbmEucm0gPSBUUlVFKSkgCgpoZWFkKE1vZGVybk1hY2JldGhfc2VudGltZW50KQpgYGAKCgojIFBsb3QgY2hhbmdlcyBpbiBzZW50aW1lbnQgYnkgQWN0CgpgYGB7cn0KTW9kZXJuTWFjYmV0aF9zZW50aW1lbnQgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCkgJT4lCiAgbXV0YXRlKHJvd25hbWUgPSBwYXJzZV9udW1iZXIocm93bmFtZSkpICU+JQogIGdncGxvdChhZXMoeCA9IHJvd25hbWUsIHkgPSBzZW50aW1lbnQsIGZpbGwgPSBhY3QpKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIGdndGl0bGUoIlNlbnRpbWVudCBBbmFseXNpcyBvZiBlYWNoIEFjdCAmIFNjZW5lIGluIE1vZGVybiBNYWNiZXRoIikKICAKYGBgCgoKIyBDb21tb24gcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHdvcmRzCgotIHdlIGhhdmUgYSB0aWR5IGRhdGEgZnJhbWUgd2l0aCBib3RoIHNlbnRpbWVudCBhbmQgd29yZAotIHdlIGNhbiBhbmFseXplIHdvcmQgY291bnRzIHRoYXQgY29udHJpYnV0ZSB0byBlYWNoIHNlbnRpbWVudAoKYGBge3J9CmJpbmdfd29yZF9jb3VudHMgPC0gCiAgTW9kZXJuTWFjYmV0aF90aWR5ICU+JQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSkgJT4lCiAgY291bnQod29yZCwgc2VudGltZW50LCBzb3J0ID0gVFJVRSkgJT4lCiAgdW5ncm91cCgpCgpiaW5nX3dvcmRfY291bnRzCmBgYAoKIyBQbG90dGluZyB3b3JkIGZyZXF1ZW5jeSBieSBzZW50aW1lbnQKCmBgYHtyfQpiaW5nX3dvcmRfY291bnRzICU+JQogIGdyb3VwX2J5KHNlbnRpbWVudCkgJT4lCiAgdG9wX24oMTApICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUod29yZCA9IHJlb3JkZXIod29yZCwgbikpICU+JQogIGdncGxvdChhZXMod29yZCwgbiwgZmlsbCA9IHNlbnRpbWVudCkpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZmFjZXRfd3JhcCh+c2VudGltZW50LCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGxhYnMoeSA9ICJDb250cmlidXRpb24gdG8gc2VudGltZW50IiwKICAgICAgIHggPSBOVUxMKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKCiMgV29yZGNsb3VkcyB3aXRoICpmZWVsaW5nKgoKLSBvcmlnaW5hbGx5LCB3ZSBtYXkgaGF2ZSBoYWQgdHJvdWJsZSBkaXNjZXJuaW5nIHdoYXQgdGhlIHRleHQgd2FzIGFib3V0IGJlY2F1c2Ugc28gbWFueSBvZiB0aGUgY29tbW9uIHdvcmRzIGRvbid0IGludm9rZSBzZW50aW1lbnQgKGUuZywgTWFjZHVmZiwgQmFucXVvLCBNYWxjb2xtKQotIGhvdyBhYm91dCB3ZSByZXZpc2l0IG91ciB3b3JkY2xvdWQgYmFzZWQgb24gcG9zaXRpdmUvbmVnYXRpdmUgc2VudGltZW50IQogICAgLSB3ZSdsbCB1c2UgYHdvcmRjbG91ZDo6Y29tcGFyaXNvbi5jbG91ZCggKWAKICAgIC0gbm90ZTogd2UnbGwgbmVlZCBgYWNhc3QoKWAgdG8gdHVybiB0aGUgZGF0YSBmcmFtZSBpbnRvIGEgbWF0cml4CiAgICAtIFE6IEFueXRoaW5nIHN0cmFuZ2UgYWJvdXQgdGhpcyB3b3JkIGNsb3VkPwogICAgICAgIAoKYGBge3IgZXZhbD1GQUxTRX0KTW9kZXJuTWFjYmV0aF90aWR5ICU+JQogIGFudGlfam9pbihzdG9wX3dvcmRzKSAlPiUKICBjb3VudCh3b3JkKSAlPiUKICB3aXRoKHdvcmRjbG91ZCh3b3JkLCBuLCBtYXgud29yZHMgPSA0NSkpCmBgYAoKCgpgYGB7cn0KcmVxdWlyZShyZXNoYXBlMikKCk1vZGVybk1hY2JldGhfdGlkeSAlPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJiaW5nIikpICU+JQogIGNvdW50KHdvcmQsIHNlbnRpbWVudCwgc29ydCA9IFRSVUUpICU+JQogIGFjYXN0KHdvcmQgfiBzZW50aW1lbnQsIHZhbHVlLnZhciA9ICJuIiwgZmlsbCA9IDApICU+JQogIGNvbXBhcmlzb24uY2xvdWQoY29sb3JzID0gYygiZ3JheTIwIiwgImdyYXk4MCIpLCBtYXgud29yZHMgPSA0NSkKYGBgCgoKCjwhLS0gIyBTZW50ZW5jZSB0b2tlbml6YXRpb24gKEFzaWRlKSAtLT4KCgo8IS0tIGBgYHtyfSAtLT4KPCEtLSBNb2Rlcm5NYWNiZXRoX3RpZHlfc2VudCA8LSAgLS0+CjwhLS0gICBNb2Rlcm5NYWNiZXRoICU+JSAtLT4KPCEtLSAgIHNlbGVjdCh0ZXh0LCBhY3QgPSBkb2N2YXIxLCBzY2VuZSA9IGRvY3ZhcjIpICU+JSAtLT4KPCEtLSAgIHVubmVzdF90b2tlbnMob3V0cHV0ID0gc2VudGVuY2UsIGlucHV0ID0gdGV4dCwgdG9rZW4gPSAic2VudGVuY2VzIikgJT4lICMgc2VudGVuY2UgdG9rZW5pemF0aW9uIC0tPgo8IS0tICAgbXV0YXRlKHNlbnRlbmNlID0gZ3N1YihwYXR0ZXJuID0gIuKAmXzigJgiLCByZXBsYWNlbWVudCA9ICInIiwgeCA9IHNlbnRlbmNlKSkgICMgZml4IHF1b3RlcyAtLT4KCjwhLS0gTW9kZXJuTWFjYmV0aF90aWR5X3NlbnQkc2VudGVuY2UgJT4lIGhlYWQoKSAtLT4KPCEtLSBgYGAgLS0+CgoKIyBQb3NpdGl2ZSBzY2VuZXMgaW4gTWFjYmV0aAoKLSB3ZSB3YW50IHRvIGZpbmQgdGhlIG1vc3QgcG9zaXRpdmUgc2NlbmUgaW4gZWFjaCBhY3QKLSB0aGUgcmF0aW9zIGFyZW4ndCBnb29kCi0gaW4gdGhlIGFjdHVhbCB0ZXh0LCB0aGluZ3MgZ28gcHJldHR5IHNvdXRoIHByZXR0eSBmYXN0Li4uCiAgICAtIEFjdCAxLCBTY2VuZSA2OiBEdW5jYW4gJiBCYW5xdW8gb24gYSBsZWlzdXJlbHkgcmlkZSB0aHJvdWdoIHRoZSBjb3VudHJ5c2lkZSAoZ2VudWluZWx5IHBsZWFzYW50KQogICAgLSBBY3QgMiwgU2NlbmUgMTogTWFjYmV0aCB0cnlpbmcgdG8gYWN0IG5hdHVyYWwgZm9yIEJhbnF1byAod2hpbGUgcGxhbm5pbmcgdG8ga2lsbCBEdW5jYW4pCiAgICAtIEFjdCAzLCBTY2VuZSAyOiBNYWNiZXRoIHdpc2hpbmcgaGUgd2FzIGRlYWQgbGlrZSBEdW5jYW4gCiAgICAtIEFjdCA0LCBTY2VuZSAyOiBzdXBlciBsb25nLCBJIGRpZG4ndCByZWFkIGl0IGFsbAogICAgLSBBY3QgNSwgU2NlbmUgNjogTWFsY29sbSAmIE1hY2R1ZmYgcHN5Y2ggdGhlbXNlbHZlcyB1cCB0byBraWxsIE1hY2JldGgKCmBgYHtyfQpiaW5ncG9zaXRpdmUgPC0gZ2V0X3NlbnRpbWVudHMoImJpbmciKSAlPiUgCiAgZmlsdGVyKHNlbnRpbWVudCA9PSAicG9zaXRpdmUiKQoKd29yZGNvdW50cyA8LSBNb2Rlcm5NYWNiZXRoX3RpZHkgJT4lCiAgZ3JvdXBfYnkoYWN0LCBzY2VuZSkgJT4lCiAgc3VtbWFyaXplKHdvcmRzID0gbigpKQoKTW9kZXJuTWFjYmV0aF90aWR5ICU+JQogIHNlbWlfam9pbihiaW5ncG9zaXRpdmUpICU+JQogIGdyb3VwX2J5KGFjdCwgc2NlbmUpICU+JQogIHN1bW1hcml6ZShwb3NpdGl2ZXdvcmRzID0gbigpKSAlPiUKICBsZWZ0X2pvaW4od29yZGNvdW50cywgYnkgPSBjKCJhY3QiLCAic2NlbmUiKSkgJT4lCiAgbXV0YXRlKHJhdGlvID0gcG9zaXRpdmV3b3JkcyAvIHdvcmRzKSAlPiUgICAgICAgICAjIHJhdGlvIG9mIHBvc2l0aXZlIHdvcmRzCiAgZmlsdGVyKHNjZW5lICE9IDApICU+JQogIHRvcF9uKDEpICU+JQogIHVuZ3JvdXAoKQpgYGAKCgojIEFuYWx5c2lzIG9mIHdvcmQgJiBkb2N1bWVudCBmcmVxdWVuY3kKCi0gYSBjZW50cmFsIHF1ZXN0aW9uIGluIG5hdHVyYWwgbGFuZ3VhZ2UgcHJvY2Vzc2luZyBpcyBob3cgdG8gcXVhbnRpZnkgd2hhdCBhIGRvY3VtZW50IGlzIGFib3V0Ci0gb25lIGFwcHJvYWNoIGFzIHdlIGhhdmUgZGlzY3Vzc2VkIGlzIHNpbXBseSB0ZXJtIGZyZXF1ZW5jeSAKICAgIC0gdGhpcyBtYXkgZHJhdyBhdHRlbnRpb24gY29tbW9uIHdvcmRzIHJlZ2FyZGxlc3Mgb2YgaW1wb3J0YW5jZQogICAgLSBzdG9wIHdvcmQgcmVtb3ZhbCBhdHRlbXB0cyB0byBhZGp1c3RpbmcgdGVybSBmcmVxdWVuY3kgZm9yIGNvbW1vbiB1c2VkIHdvcmRzCiAgICAtIHRoaXMgaXMgYSBmYWlybHkgY3J1ZGUgYXBwcm9hY2g7IHBlcmhhcHMgd2UgY2FuIGRvIGJldHRlcgoKIyBEb2N1bWVudCB0ZXJtIG1hdHJpY2VzCgotIGFsdGVybmF0aXZlIGFwcHJvYWNoIGZvciBhc3Nlc3NpbmcgdGVybSBmcmVxdWVuY3kgaXMgdGhlICoqZG9jdW1lbnQtdGVybSBtYXRyaXgqKgogICAgLSBzcGFyc2UgbWF0cml4IGRlc2NyaWJpbmcgYSBjb2xsZWN0aW9uIChjb3JwdXMpIG9mIGRvY3VtZW50cwogICAgLSByb3cgZm9yIGVhY2ggZG9jdW1lbnQKICAgIC0gY29sdW1uIGZvciBlYWNoIHRlcm0KICAgIC0gdmFsdWVzIGFyZSB0eXBpY2FsbHkgd29yZCBjb3VudCBvciAidGYtaWRmIiAoc2VlIGJlbG93KQotICoqdGVybSBmcmVxdWVuY3kqKi0tJHRmKHQsIGQpJC0taXMgc2ltcGx5IHRoZSBmcmVxdWVuY3kgb2YgdGVybSAkdCQgaW4gZG9jdW1lbnQgJGQkCi0gKippbnZlcnNlIGRvY3VtZW50IGZyZXF1ZW5jeSoqLS0kaWRmJC0taXMgcHJldmFsZW5jZSBvZiB0ZXJtICR0JCBhY3Jvc3MgYSBzZXQgb2YgZG9jdW1lbnRzICREJAogICAgLSB3b3JkcyB0aGF0IGFyZSBjb21tb24gYWNyb3NzIG1hbnkgZG9jdW1lbnRzIGxpa2UgInRoZSIgZ2V0IGxvdyByYW5rCiAgICAtIG51bWVyYXRvciBpcyB0aGUgbnVtYmVyIG9mIGRvY3VtZW50cyBpbiAkRCQKICAgIC0gZGVub21pbmF0b3IgaXMgdGhlIG51bWJlciBvZiBkb2N1bWVudHMgaW4gJEQkIGNvbnRhaW5pbmcgdGhlIHRlcm0KLSAkdGYtaWRmJCBtdWx0aXBsaWVzIHRoZSB0d28KICAgIC0gZnJlcXVlbmN5IG9mIGEgdGVybSBhZGp1c3RlZCBmb3IgaG93IHJhcmVseSBpdCBpcyB1c2VkCiAgICAtIHJvdWdobHkgbWVhc3VyZXMgaG93IGltcG9ydGFudCBhIHdvcmQgaXMgdG8gYSBkb2N1bWVudCBpbiBhIGNvbGxlY3Rpb24gKG9yIGNvcnB1cykgb2YgZG9jdW1lbnRzCgpcW2lkZih0LCBEKSA9IFx0ZXh0e2xufVxmcmFje3xEfH17fFx7ZCBcaW4gRCA6IHQgXGluIGRcfXx9XF0KCgojIFRlcm0gZnJlcXVlbmN5IGluIEFjdHMgb2YgTWFjYmV0aAoKLSBXaGF0IGFyZSB0aGUgbW9zdCBjb21tb25seSB1c2VkIHdvcmRzIGluIGVhY2ggQWN0PyAKICAgIC0gdGVybSBmcmVxdWVuY3kKICAgIC0gJHRmLWlkZiQKLSB3ZSB3aWxsICJzdGFydCBvdmVyIiBmcm9tIHRoZSBzb3VyY2UgKHdpdGhvdXQgcmVtb3Zpbmcgc3RvcCB3b3JkcykKLSBlYWNoIHJvdyBpbiBgYWN0X3dvcmRzYCBpcyBhIHVuaXF1ZSB3b3JkLWFjdCBjb21iaW5hdGlvbgogICAgLSBgbmAgaXMgdGhlIG51bWJlciBvZiB0aW1lcyB0aGF0IHdvcmQgaXMgdXNlZCBpbiB0aGF0IGFjdCAKICAgIC0gYHRvdGFsYCBpcyB0aGUgdG90YWwgd29yZHMgaW4gdGhlIGFjdAoKYGBge3J9CiMgdGYgd2l0aGluIGVhY2ggYWN0CmFjdF93b3JkcyA8LSAKICBNb2Rlcm5NYWNiZXRoICU+JQogIHNlbGVjdCh0ZXh0LCBhY3QgPSBkb2N2YXIxLCBzY2VuZSA9IGRvY3ZhcjIpICU+JQogIHVubmVzdF90b2tlbnMob3V0cHV0ID0gd29yZCwgaW5wdXQgPSB0ZXh0KSAlPiUKICBtdXRhdGUod29yZCA9IGdzdWIocGF0dGVybiA9ICLigJkiLCByZXBsYWNlbWVudCA9ICInIiwgeCA9IHdvcmQpKSAlPiUKICBjb3VudChhY3QsIHdvcmQsIHNvcnQgPSBUUlVFKQoKIyB0b3RhbCB3b3JkcyBpbiB0aGUgYWN0CnRvdGFsX3dvcmRzIDwtIAogIGFjdF93b3JkcyAlPiUgCiAgZ3JvdXBfYnkoYWN0KSAlPiUgCiAgc3VtbWFyaXplKHRvdGFsID0gc3VtKG4pKQoKYWN0X3dvcmRzIDwtIGxlZnRfam9pbihhY3Rfd29yZHMsIHRvdGFsX3dvcmRzKQphY3Rfd29yZHMKYGBgCgoKIyBUZXJtIGZyZXF1ZW5jeSBpbiBNYWNiZXRoCgotIGxldOKAmXMgbG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGBuIC8gdG90YWxgIGZvciBlYWNoIGFjdAotIHRoaXMgaXMgdGhlIHRlcm0gZnJlcXVlbmN5LCAkdGYkIAotIHZlcnkgbG9uZyByaWdodCB0YWlscyAodGhvc2UgZXh0cmVtZWx5IGNvbW1vbiB3b3JkcykgCi0gVGhlc2UgcGxvdHMgZXhoaWJpdCBzaW1pbGFyIGRpc3RyaWJ1dGlvbnMgZm9yIGFsbCB0aGUgYWN0cyAKICAgIC0gbWFueSB3b3JkcyB0aGF0IG9jY3VyIHJhcmVseQogICAgLSBmZXdlciB3b3JkcyB0aGF0IG9jY3VyIGZyZXF1ZW50bHkKCgpgYGB7cn0KYWN0X3dvcmRzICU+JSAKICBnZ3Bsb3QoYWVzKG4gLyB0b3RhbCwgZmlsbCA9IGFjdCkpICsKICBnZW9tX2hpc3RvZ3JhbShzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZmFjZXRfd3JhcCh+IGFjdCwgbmNvbCA9IDIsIHNjYWxlcyA9ICJmcmVlX3kiKSArIAogIGdndGl0bGUoIlRlcm0gZnJlcXVlbmN5IGluIEFjdHMgb2YgTWFjYmV0aCIpCmBgYAoKCgojIFppcGYncyBsYXcKCi0gbG9uZy10YWlsZWQgZGlzdHJpYnV0aW9ucyBhcmUgY29tbW9uIGluIGFueSBnaXZlbiBjb3JwdXMgb2YgbmF0dXJhbCBsYW5ndWFnZSAobGlrZSBhIGJvb2ssIG9yIGEgbG90IG9mIHRleHQgZnJvbSBhIHdlYnNpdGUsIG9yIHNwb2tlbiB3b3JkcykgCi0gYSBjbGFzc2ljIHZlcnNpb24gb2YgdGhpcyByZWxhdGlvbnNoaXAgaXMgY2FsbGVkIFppcGbigJlzIGxhdywgYWZ0ZXIgR2VvcmdlIFppcGYsIGEgMjB0aCBjZW50dXJ5IEFtZXJpY2FuIGxpbmd1aXN0LgotICoqWmlwZuKAmXMgbGF3Kiogc3RhdGVzIHRoYXQgdGhlIGZyZXF1ZW5jeSB0aGF0IGEgd29yZCBhcHBlYXJzIGlzIGludmVyc2VseSBwcm9wb3J0aW9uYWwgdG8gaXRzIHJhbmsuCgpgYGB7cn0KZnJlcV9ieV9yYW5rIDwtIAogIGFjdF93b3JkcyAlPiUgCiAgZ3JvdXBfYnkoYWN0KSAlPiUgCiAgbXV0YXRlKHJhbmsgPSByb3dfbnVtYmVyKCksICAgIyBzaW5jZSBhbHJlYWR5IG9yZGVyZWQgYnkgYG5gCiAgICAgICAgIGB0ZXJtIGZyZXF1ZW5jeWAgPSBuIC8gdG90YWwpCgpmcmVxX2J5X3JhbmsKYGBgCgoKCiMgVmlzdWFsaXphdGlvbiBvZiBaaXBmJ3MgbGF3CgotIFppcGbigJlzIGxhdyB2aXN1YWxpemVkIGJ5IHBsb3R0aW5nIChvbiBsb2dhcml0aG1pYyBzY2FsZXMpCiAgICAtIHgtYXhpczogYHJhbmtgIG9mIGVhY2ggd29yZCB3aXRoaW4gdGhlIGZyZXF1ZW5jeSB0YWJsZQogICAgLSB5LWF4aXM6IGB0ZXJtIGZyZXF1ZW5jeWAgb24gdGhlIHktYXhpcwogICAgLSByZXN1bHQ6IGludmVyc2VseSBwcm9wb3J0aW9uYWwgcmVsYXRpb25zaGlwIGhhcyBhIGNvbnN0YW50LCBuZWdhdGl2ZSBzbG9wZQotIHRoaXMgdHlwZSBvZiByZXN1bHQgaXMga25vd24gYXMgYSAqKnBvd2VyIGxhdyoqIDxodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Qb3dlcl9sYXc+CiAgICAtIHJlbGF0aXZlIGNoYW5nZSBpbiBvbmUgcXVhbnRpdHkgcmVzdWx0cyBpbiBhIHByb3BvcnRpb25hbCByZWxhdGl2ZSBjaGFuZ2UgaW4gdGhlIG90aGVyIHF1YW50aXR5LCBpbmRlcGVuZGVudCBvZiB0aGUgaW5pdGlhbCBzaXplIG9mIHRob3NlIHF1YW50aXRpZXMKICAgIC0gb25lIHF1YW50aXR5IHZhcmllcyBhcyBhIHBvd2VyIG9mIGFub3RoZXIKICAgIC0gYXJlYSBvZiBhIHNxdWFyZSB2cyBsZW5ndGggb2Ygc2lkZTsgMiB0aW1lcyBsZW5ndGggPj4gJDJeMiQgdGltZXMgdGhlIGFyZWEKCgpgYGB7cn0KZnJlcV9ieV9yYW5rICU+JSAKICBnZ3Bsb3QoYWVzKHJhbmssIGB0ZXJtIGZyZXF1ZW5jeWAsIGNvbG9yID0gYWN0KSkgKyAKICBnZW9tX2xpbmUoc2l6ZSA9IDEuMSwgYWxwaGEgPSAwLjgsIHNob3cubGVnZW5kID0gVFJVRSkgKyAKICBzY2FsZV94X2xvZzEwKCkgKwogIHNjYWxlX3lfbG9nMTAoKSArIAogIGdndGl0bGUoIlppcGYncyBsYXcgZm9yIGFjdHMgb2YgTWFjYmV0aCIpCmBgYAoKCiMgSW52ZXN0aWdhdGluZyB0ZXJtIGZyZXF1ZW5jeSBpbiBNYWNiZXRoCgotIHJlc3VsdCB3YXNuJ3QgKnF1aXRlKiBsaW5lYXIKLSBkZXZpYXRpb25zIGF0IGhpZ2ggcmFuayBhcmUgbm90IHVuY29tbW9uIGZvciBtYW55IGtpbmRzIG9mIGxhbmd1YWdlOyAKICAgIC0gYSBjb3JwdXMgb2Z0ZW4gY29udGFpbnMgZmV3ZXIgcmFyZSB3b3JkcyB0aGFuIHByZWRpY3RlZCBieSBhIHNpbmdsZSBwb3dlciBsYXcuIAotIGRldmlhdGlvbnMgYXQgbG93IHJhbmsgYXJlIG1vcmUgdW51c3VhbCAKICAgIC0gTW9kZXJuIE1hY2JldGggdXNlZCBhIGxvd2VyIHBlcmNlbnRhZ2Ugb2YgdGhlIG1vc3QgY29tbW9uIHdvcmRzIHRoYW4gbWFueSBjb2xsZWN0aW9ucyBvZiBsYW5ndWFnZS4KICAgIC0gYW5hbHlzaXMgY291bGQgYmUgZXh0ZW5kZWQgdG8gY29tcGFyZSBhdXRob3JzLCBvciBvdGhlciBjb2xsZWN0aW9ucyBvZiB0ZXh0IChzb25uZXRzIHZzIHRyYWdlZGllcykKCgpgYGB7cn0KcmFua19zdWJzZXQgPC0gZnJlcV9ieV9yYW5rICU+JSAKICBmaWx0ZXIocmFuayA8IDIwMCwKICAgICAgICAgcmFuayA+IDEwKQoKbG0obG9nMTAoYHRlcm0gZnJlcXVlbmN5YCkgfiBsb2cxMChyYW5rKSwgZGF0YSA9IHJhbmtfc3Vic2V0KQoKZnJlcV9ieV9yYW5rICU+JSAKICBnZ3Bsb3QoYWVzKHJhbmssIGB0ZXJtIGZyZXF1ZW5jeWAsIGNvbG9yID0gYWN0KSkgKyAKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAtMC45NDYsIHNsb3BlID0gLTAuOTA5LCBjb2xvciA9ICJncmF5NTAiLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX2xpbmUoc2l6ZSA9IDEuMSwgYWxwaGEgPSAwLjgsIHNob3cubGVnZW5kID0gVFJVRSkgKyAKICBzY2FsZV94X2xvZzEwKCkgKwogIHNjYWxlX3lfbG9nMTAoKSArIAogIGdndGl0bGUoIlppcGYncyBsYXcgZm9yIE1vZGVybiBNYWNiZXRoIikKYGBgCgo8IS0tIERheTYgLS0+CgojICp0Zi1pZGYqCgotICAqKmlkZWEgb2YgKnRmLWlkZioqKjogZmluZCBpbXBvcnRhbnQgd29yZHMgZm9yIHRoZSBjb250ZW50IG9mIGVhY2ggZG9jdW1lbnQgYnkKICAgIC0gKmRlY3JlYXNpbmcqIHRoZSB3ZWlnaHQgZm9yIGNvbW1vbmx5IHVzZWQgd29yZHMgCiAgICAtICppbmNyZWFzaW5nKiB0aGUgd2VpZ2h0IGZvciB3b3JkcyB0aGF0IGFyZSBub3QgdXNlZCB2ZXJ5IG11Y2ggaW4gdGhlIGNvcnB1cyBvZiBkb2N1bWVudHMKLSBDYWxjdWxhdGluZyAkdGYtaWRmJCBhdHRlbXB0cyB0byBmaW5kIHRoZSB3b3JkcyB0aGF0IGFyZSBpbXBvcnRhbnQgKGkuZS4sIGNvbW1vbikgaW4gYSB0ZXh0LCBidXQgbm90IHRvbyBjb21tb24gYWNyb3NzIGFsbCB0ZXh0cy4KLSBgYmluZF90Zl9pZGYoIClgIGZ1bmN0aW9uIGluIHRoZSBgdGlkeXRleHRgIHBhY2thZ2UgdGFrZXMgYSB0aWR5IHRleHQgZGF0YXNldCBhcyBpbnB1dAogICAgLSBvbmx5IG5lZWQgb25lIHJvdyBwZXIgdG9rZW4gKHRlcm0pLCBwZXIgZG9jdW1lbnQKICAgIC0gY29sdW1uIGZvciB0aGUgd29yZHMsIGFub3RoZXIgaWRlbnRpZnlpbmcgc291cmNlIGRvY3VtZW50IChhY3QpCiAgICAtIHdlIGNhbGN1bGF0ZWQgYHRvdGFsYCBmb3IgZWFjaCBhY3QgcHJldmlvdXNseSwgYnV0IGRvbid0IG5lZWQgaXQKLSAkaWRmJAogICAgLSB6ZXJvIGZvciB3b3JkcyB0aGF0IGFyZSBjb21tb24gYWNyb3NzIGFsbCBkb2N1bWVudHMgKCR0Zi1pZGYkIGlzIHRoZW4gemVybyB0b28pCiAgICAtIGhpZ2hlciBmb3Igd29yZHMgdGhhdCBhcHBlYXIgaW4gZmV3ZXIgZG9jdW1lbnRzCgoKYGBge3J9CmFjdF93b3JkcyA8LSAKICBhY3Rfd29yZHMgJT4lCiAgYmluZF90Zl9pZGYod29yZCwgYWN0LCBuKQoKYWN0X3dvcmRzCmBgYAoKIyBIaWdoICp0Zi1pZGYqIGluIE1hY2JldGgKCi0gY29tbW9uIHRvIHNlZSBwcm9wZXIgbm91bnMgKHBlb3BsZS9wbGFjZXMpIGZyb20gZWFjaCBkb2N1bWVudCB3aXRoIHRoZSBoaWdoZXN0ICp0Zi1pZGYqCi0gaW50ZXJlc3RpbmcgaGVyZSB0byBzZWUgb3RoZXIgZmVhdHVyZWQgdG9rZW5zIChjYXVsZHJvbiwgaGFpbCwgZXRjKSEgCi0gbm90ZTogZGlzY3JldGVuZXNzIGluIHRoZSAkaWRmJCBoZXJlIGJlY2F1c2Ugd2UgaGF2ZSBvbmx5IDUgZG9jdW1lbnRzCiAgICAtIHRlcm0gdGhhdCBhcHBlYXJzIGluIG9ubHkgb25lIG9mIHRoZSBmaXZlIGFjdHM6ICRpZGYgPSBsbig1LzEpID0gMS42MDk0JAoKYGBge3J9CmFjdF93b3JkcyAlPiUKICBzZWxlY3QoLXRvdGFsKSAlPiUKICBhcnJhbmdlKGRlc2ModGZfaWRmKSkKYGBgCgoKIyBWaXN1YWxpemluZyBoaWdoICp0Zi1pZGYqIHdvcmRzCgpgYGB7cn0KYWN0X3dvcmRzICU+JQogIGFycmFuZ2UoZGVzYyh0Zl9pZGYpKSAlPiUKICBtdXRhdGUod29yZCA9IGZhY3Rvcih3b3JkLCBsZXZlbHMgPSByZXYodW5pcXVlKHdvcmQpKSkpICU+JSAKICBncm91cF9ieShhY3QpICU+JSAKICB0b3BfbigxMCkgJT4lIAogIHVuZ3JvdXAoKSAlPiUKICBnZ3Bsb3QoYWVzKHdvcmQsIHRmX2lkZiwgZmlsbCA9IGFjdCkpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgbGFicyh4ID0gTlVMTCwgeSA9ICJ0Zi1pZGYiKSArCiAgZmFjZXRfd3JhcCh+YWN0LCBuY29sID0gMiwgc2NhbGVzID0gImZyZWUiKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKCgoKIyBOb3RlIGFib3V0IGNvbnZlcnRpbmcgdG8vZnJvbSBvdGhlciBub24tdGlkeSBmb3JtYXRzCgotIHRoZXJlIGFyZSBsb3RzIG9mIHBhY2thZ2VzIGZvciB0ZXh0IGFuYWx5c2lzLCBlLmcuLCAKICAgIC0gYHF1YW50ZWRhYAogICAgLSBgdG1gCi0gdGhlc2Ugb2Z0ZW4gcmVseSBvbiBhIGNvbW1vbiAobm9uLXRpZHkpIGRhdGEgc3RydWN0dXJlIGNhbGxlZCBhICJkb2N1bWVudC10ZXJtIG1hdHJpeCIKLSAqKkRvY3VtZW50LXRlcm0gbWF0cml4IChEVE0pKiogaXMgYSBtYXRyaXggc3VjaCB0aGF0CiAgICAtIGVhY2ggKnJvdyogcmVwcmVzZW50cyBvbmUgZG9jdW1lbnQgKGJvb2ssIGFydGljbGUsIHR3ZWV0LCBldGMpCiAgICAtIGVhY2ggKmNvbHVtbiogcmVwcmVzZW50cyBvbmUgdGVybQogICAgLSBlYWNoIHZhbHVlICh0eXBpY2FsbHkpIGNvbnRhaW5zIGZyZXF1ZW5jeSBvZiB0aGUgdGVybSBpbiB0aGUgZG9jdW1lbnQKCgojIERvY3VtZW50LXRlcm0gbWF0cml4CgotIERUTSdzIGFyZSB1c3VhbGx5IHZlcnkgc3BhcnNlIHNpbmNlIGxvdHMgb2YgdGVybS1kb2N1bWVudCBwYWlycyBkb24ndCBvY2N1cgotIERUTSdzIGRvbid0IHBsYXkgbmljZSB3aXRoIHRpZHkgdG9vbHMgJiBkYXRhIGZyYW1lcyBkb24ndCBwbGF5IG5pY2Ugd2l0aCBtb3N0IHRleHQgbWluaW5nIHBhY2thZ2VzCiAgICAtIGB0aWR5dGV4dGAgcGFja2FnZSBoYXMgdG9vbHMgdG8gZWFzaWx5IGNvbnZlcnQgYmV0d2VlbiBvYmplY3QgdHlwZXMKICAgIC0gYHRpZHkoKWAgdHVybnMgYSBEVE0gaW50byBhIHRpZHkgZGF0YSBmcmFtZQogICAgLSBgY2FzdCgpX2R0bWAgdHVybnMgYSB0aWR5LCBvbmUtdGVybS1wZXItcm93LCBkYXRhIGZyYW1lIGludG8gYSBEVE0gKGZvciB0b29scyBpbiBgdG1gIHBrZykKICAgIC0gYGNhc3QoKV9kdG1gICh3aXRoIGEgIlQiKSB0dXJucyBhIHRpZHksIG9uZS10ZXJtLXBlci1yb3csIGRhdGEgZnJhbWUgaW50byBhIERUTSAoZm9yIHRvb2xzIGluIGB0bWAgcGtnKQogICAgLSBgY2FzdCgpX2RmbWAgKHdpdGggYW4gIkYiKSB0dXJucyBhIHRpZHksIG9uZS10ZXJtLXBlci1yb3csIGRhdGEgZnJhbWUgaW50byBhIERGTSAoZm9yIHRvb2xzIGluIGBxdWFudGVkYWAgcGtnKQoKCgojIFRvcGljIE1vZGVsaW5nCgotIHNvbWV0aW1lcyB1c2VmdWwgdG8gZmluZCBuYXR1cmFsIGdyb3VwcyBhbW9uZyBkb2N1bWVudHMgb2Ygc29tZSBjb2xsZWN0aW9uL2NvcnB1cyAoYmxvZyBwb3N0cywgd2lraXBlZGlhIHBhZ2VzLCBldGMpIAotICoqVG9waWMgbW9kZWxpbmcqKi0tbWV0aG9kIGZvciB1bnN1cGVydmlzZWQgY2xhc3NpZmljYXRpb24gb2YgZG9jdW1lbnRzCiAgICAtIGZpbmQgbmF0dXJhbCBncm91cHMgZXZlbiBpZiB3ZSdyZSBub3QgdG90YWxseSBzdXJlIHdoYXQgdG8gbG9vayBmb3IKICAgIC0gc2ltaWxhciB0byBjbHVzdGVyaW5nIG9uIG51bWVyaWMgZGF0YSAKLSBMYXRlbnQgRGlyaWNobGV0IGFsbG9jYXRpb24gKExEQSkgaXMgYSBwYXJ0aWN1bGFybHkgcG9wdWxhciBtZXRob2QKCgojIExhdGVudCBEaXJpY2hsZXQgYWxsb2NhdGlvbiAoTERBKQoKLSAqKkxhdGVudCBEaXJpY2hsZXQgYWxsb2NhdGlvbiAoTERBKSoqIAogICAgLSBldmVyeSBkb2N1bWVudCBpcyBhIG1peHR1cmUgb2YgKnRvcGljcyoKICAgIC0gZXZlcnkgdG9waWMgaXMgYSBtaXh0dXJlIG9mICp3b3JkcyoKICAgIC0gTERBIGJhc2ljYWxseSBlc3RpbWF0ZXMgdGhlIGNvbXBvc2l0aW9uIG9mIGJvdGggbWl4dHVyZXMgYXQgdGhlIHNhbWUgdGltZQotIHJlc3VsdCBhbGxvd3MgY29udGVudCBvZiBkb2N1bWVudHMgdG8gIm92ZXJsYXAiIHJhdGhlciB0aGFuIGVuZm9yY2luZyBkaXNjcmV0ZSBncm91cHMuLi5qdXN0IGxpa2Ugd2UgZG8gaW4gbmF0dXJhbCBsYW5ndWFnZSB1c2UuCi0gdGhlIGB0b3BpY21vZGVsc2AgcGFja2FnZSB3aWxsIGhlbHAgdXMgb24gb3VyIHdheQoKCgojIFByZXBhcmluZyBmb3IgTERBCgotIFdlJ2xsIGJvcnJvdyBhIGRhdGEgc2V0IG9mIGFydGljbGVzIGZyb20gdGhlIEFzc29jaWF0ZWQgUHJlc3MgdG8gaWxsdXN0cmF0ZSBzb21lIHByaW5jaXBsZXMgZmlyc3QuICAKICAgIC0gRG9uJ3Qgd29ycnksIGJhY2sgdG8gTWFjYmV0aCBpbiBhIGJpdAotIE5vdGU6IFdlIG5lZWQgdGhlIGRhdGEgYXMgYSBEVE0gKG5vdCB0aWR5IGZvcm0pCi0gUTogY2FuIHlvdSBleHBsYWluIHdoYXQncyBoYXBwZW5pbmcgYXQgZWFjaCBsaW5lPwotIFE6IGhvdyBtYW55IHJvd3MgJiBjb2x1bW5zIGFyZSBpbiBvdXIgYEFzc29jaWF0ZWRQcmVzc2AgbWF0cml4PwoKYGBge3J9CmRhdGEoIkFzc29jaWF0ZWRQcmVzcyIpCkFzc29jaWF0ZWRQcmVzcwpgYGAKCiMgVHdvLXRvcGljIExhdGVudCBEaXJpY2hsZXQgYWxsb2NhdGlvbiAoTERBKSBtb2RlbAoKLSBmaXR0aW5nIHRoZSBMREEgbW9kZWwgaXMgdGhlIGVhc3kgcGFydCB3aXRoIGhlbHAgb2YgdGhlIGBMREEoIClgIGZ1bmN0aW9uCi0gbm90ZTogd2Ugc2V0IGFuIGFyYml0cmFyeSBzZWVkIGluIHRoaXMgY2FzZSBzbyB3ZSBoYXZlIHNhbWUgcmVzdWx0IGVhY2ggdGltZS4uLiBub3QgbmVjZXNzYXJ5IGluIGdlbmVyYWwKLSBUaGVyZSBhcmUgYWxtb3N0IGNlcnRhaW5seSBtb3JlIHRoYW4gdHdvIHRvcGljcywgYnV0IHRoaXMgaXMgYSBzdGFydAotIEZvciBhIGNvcnB1cyBvZiBuZXdzIGFydGljbGVzIGxpa2UgdGhlIEFQIGRhdGEsIHdlIG1pZ2h0IGV4cGVjdCB0aGUgdG9waWNzIHRvIGJlIHNvbWV0aGluZyBsaWtlICJwb2xpdGljcyIgYW5kICJlbnRlcnRhaW5tZW50Ii4uLiByZWNhbGw6IAogICAgLSBlYWNoIGRvY3VtZW50IGNvdWxkIGluY2x1ZGUgYSBtaXggb2YgdG9waWNzIChwb2xpdGljcyAmIGVudGVydGFpbm1lbnQpCiAgICAtIGVhY2ggdG9waWMgaGFzIGEgbWl4IG9mIHdvcmRzCiAgICAgICAgLSAqcG9saXRpY3MqIHRvcGljIG1pZ2h0IGluY2x1ZGUgd29yZHMgbGlrZSAncHJlc2lkZW50JywgJ2NvbmdyZXNzJywgYW5kICdnb3Zlcm5tZW50CiAgICAgICAgLSAqZW50ZXJ0YWlubWVudCogdG9waWMgbWlnaHQgaW5jbHVkZSAnbW92aWVzJywgJ3RlbGV2aXNpb24nLCBhbmQgJ2FjdG9yJwogICAgICAgIC0gKipib3RoKiogdG9waWNzIG1pZ2h0IGZyZXF1ZW50bHkgaW5jbHVkZSBhIHdvcmQgbGlrZSAnYnVkZ2V0JwoKYGBge3J9CnJlcXVpcmUodG9waWNtb2RlbHMpCgphcF9sZGEgPC0gCiAgQXNzb2NpYXRlZFByZXNzICU+JQogIExEQShrID0gMiwgY29udHJvbCA9IGxpc3Qoc2VlZCA9IDEyMzQpKSAgCgphcF9sZGEKYGBgCgojIFBlci10b3BpYy1wZXItd29yZCBwcm9iYWJpbGl0aWVzIAoKLSB3YW50IHRvIGV4dHJhY3QgdGhlIHBlci10b3BpYy1wZXItd29yZCBwcm9iYWJpbGl0aWVzLCAkXGJldGEkICgiYmV0YSIpLCBmcm9tIHRoZSBtb2RlbAotIGB0aWR5dGV4dDo6dGlkeSggKWAgcHJvdmlkZXMgbWV0aG9kIAogICAgLSBvbmUtdG9waWMtcGVyLXRlcm0tcGVyLXJvdyBmb3JtYXQKICAgIC0gRm9yIGVhY2ggY29tYmluYXRpb24sIG1vZGVsIGNvbXB1dGVzIHByb2JhYmlsaXR5IG9mIHRlcm0gYmVpbmcgZ2VuZXJhdGVkIGZyb20gdGhhdCB0b3BpYwoKYGBge3J9CmFwX3RvcGljcyA8LSB0aWR5KGFwX2xkYSwgbWF0cml4ID0gImJldGEiKQphcF90b3BpY3MKYGBgCgoKIyBUb3AgMTAgcGVyLXRvcGljLXBlci13b3JkIHByb2JhYmlsaXRpZXMgKCRcYmV0YSQpIAoKLSBROiBob3cgbWlnaHQgeW91IGNoYXJhY3Rlcml6ZSBlYWNoIHRvcGljIG5vdz8KICAgIC0gbm90ZSB3b3JkcyBjaGFyYWN0ZXJpc3RpYyBvZiBlYWNoIHRvcGljCiAgICAtIG5vdGUgd29yZHMgY29tbW9uIHRvIGJvdGggdG9waWNzCgpgYGB7cn0KYXBfdG9wX3Rlcm1zIDwtIGFwX3RvcGljcyAlPiUKICBncm91cF9ieSh0b3BpYykgJT4lCiAgdG9wX24oMTAsIGJldGEpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKHRvcGljLCAtYmV0YSkKCmFwX3RvcF90ZXJtcyAlPiUKICBtdXRhdGUodGVybSA9IHJlb3JkZXIodGVybSwgYmV0YSkpICU+JQogIGdncGxvdChhZXModGVybSwgYmV0YSwgZmlsbCA9IGZhY3Rvcih0b3BpYykpKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGZhY2V0X3dyYXAofiB0b3BpYywgc2NhbGVzID0gImZyZWUiKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKCiMgV29yZHMgd2l0aCBncmVhdGVzdCBkaWZmZXJlbmNlIGluICRcYmV0YSQgYmV0d2VlbiB0b3BpY3MKCi0gZXN0aW1hdGVkIGJhc2VkIG9uIHRoZSBsb2cgcmF0aW8gb2YgdGhlIHR3bzogJFx0ZXh0e2xvZ31fMihcZnJhY3tcYmV0YV8yfXtcYmV0YV8xfSkkCi0gYSBsb2cgcmF0aW8gaXMgdXNlZnVsIGJlY2F1c2UgaXQgbWFrZXMgdGhlIGRpZmZlcmVuY2Ugc3ltbWV0cmljYWwKICAgIC0gJFxiZXRhXzIkIGJlaW5nIHR3aWNlIGFzIGxhcmdlIGxlYWRzIHRvIGEgbG9nIHJhdGlvIG9mIDEKICAgIC0gJFxiZXRhXzEkIGJlaW5nIHR3aWNlIGFzIGxhcmdlIHJlc3VsdHMgaW4gLTEKLSB3ZSBmaWx0ZXIgZm9yIHJlbGF0aXZlbHkgY29tbW9uIHdvcmRzIHdpdGggJFxiZXRhID4gMS8xMDAwJCBpbiBhdCBsZWFzdCBvbmUgdG9waWMKLSBROiB3aGF0IGNhbiB3ZSBsZWFybiBieSBpbnNwZWN0aW5nIHRoZSBleHRyZW1lcz8KCmBgYHtyfQpiZXRhX3NwcmVhZCA8LSBhcF90b3BpY3MgJT4lCiAgbXV0YXRlKHRvcGljID0gcGFzdGUwKCJ0b3BpYyIsIHRvcGljKSkgJT4lCiAgc3ByZWFkKHRvcGljLCBiZXRhKSAlPiUKICBmaWx0ZXIodG9waWMxID4gLjAwMSB8IHRvcGljMiA+IC4wMDEpICU+JQogIG11dGF0ZShsb2dfcmF0aW8gPSBsb2cyKHRvcGljMiAvIHRvcGljMSkpICU+JQogIGFycmFuZ2UoZGVzYyhsb2dfcmF0aW8pKQoKYmV0YV9zcHJlYWQKYGBgCgoKCiMgUGVyLWRvY3VtZW50IGNsYXNzaWZpY2F0aW9uCgotIExEQSBhbHNvIG1vZGVscyBlYWNoIGRvY3VtZW50IGFzIGEgbWl4dHVyZSBvZiB0b3BpY3MKLSBXZSBleGFtaW5lIHRoZSBwZXItZG9jdW1lbnQtcGVyLXRvcGljIHByb2JhYmlsaXRpZXMsICRcZ2FtbWEkICgiZ2FtbWEiKQotIEdhbW1hIGlzIHByb3BvcnRpb24gb2Ygd29yZHMgZnJvbSB0aGF0IGRvY3VtZW50LCBnZW5lcmF0ZWQgZnJvbSB0aGF0IHRvcGljLiAgRm9yIGV4YW1wbGUsCiAgICAtIGFib3V0IDI1JSBvZiB0aGUgd29yZHMgaW4gZG9jdW1lbnQgMSBjb21lIGZyb20gdG9waWMgMQogICAgLSBhYm91dCA4MiUgb2YgdGhlIHdvcmRzIGluIGRvY3VtZW50IDUgY29tZSBmcm9tIHRvcGljIDIKCmBgYHtyfQpzY2VuZXNfZ2FtbWEgPC0gdGlkeShhcF9sZGEsIG1hdHJpeCA9ICJnYW1tYSIpCnNjZW5lc19nYW1tYSAlPiUgCiAgYXJyYW5nZShkb2N1bWVudCwgZ2FtbWEpCmBgYAoKIyBMZXQncyBpbnZlc3RpZ2F0ZSBhIGZldyBpbnRlcmVzdGluZyBkb2N1bWVudHMKCi0gRG9jdW1lbnQgMyBpcyBhbG1vc3QgNTAtNTAgYmV0d2VlbiBvdXIgdHdvIHRvcGljcwotIERvY3VtZW50IDYgaXMgYWxtb3N0IGVudGlyZWx5IHRvcGljIDIKLSBROiB3aGF0IGRvZXMgZWFjaCBkb2N1bWVudCBzZWVtIHRvIGJlIGFib3V0PwotIFE6IGRvZXMgJFxnYW1tYSQgYXR0cmlidXRlZCB0byBlYWNoIHRvcGljIG1ha2Ugc2Vuc2UgaW4gdGhlc2UgY2FzZXM/CgpgYGB7cn0KIyBpbnZlc3RpZ2F0ZSBkb2N1bWVudCAzICYgNgp0aWR5KEFzc29jaWF0ZWRQcmVzcykgJT4lCiAgZmlsdGVyKGRvY3VtZW50ID09IDMpICU+JQogIGFycmFuZ2UoZGVzYyhjb3VudCkpCmBgYAoKCiMgTW9kZWxpbmcgVG9waWNzIGFzIEFjdHMgaW4gTWFjYmV0aC4uLj8KCi0gUmVjYWxsOiBvdXIgbW90aXZhdGluZyBxdWVzdGlvbiB3YXMgdG8gdHJ5IGFuZCBsZWFybiBpZiB3ZSBjb3VsZCB1c2UgdGhlIGNvbnRlbnQgb2YgTWFjYmV0aCB0byBleHBvc2Ugc3RydWN0dXJlIGFtb25nIHRoZSA1IGFjdHMKLSBSZXN1bHRzIHNvIGZhcjogCiAgICAtIHdvcmRjbG91ZHM6ICJtZWgiCiAgICAtIHNlbnRpbWVudCBhbmFseXNpczogc3VwZXIgbmVnYXRpdmUuLi5ubyB3b25kZXIgTWFjYmV0aCBpcyBjYWxsZWQgYSAidHJhZ2VkeSIKICAgIC0gKnRmLWlkZio6IGhhcmQgdG8gdGVsbCBtdWNoLi4uIGFsbCB0aGUgZG9jdW1lbnRzIHdlcmUgd3JpdHRlbiBieSB0aGUgc2FtZSBwZXJzb24gZm9yIHRoZSBzYW1lIHB1cnBvc2UsIHNvIG5vdCBzdXJwcmlzZWQKICAgIC0gKipMREEqKjogPz8/Ci0gR29hbDogdXNlIExEQSB0byBtb2RlbCBgayA9IDVgIHRvcGljcyAKICAgIC0gY29tbW9uIHRvIHRyeSBhIGZldyBkaWZmZXJlbnQgdmFsdWVzIG9mIGBrYCAobnVtYmVyIG9mIHRvcGljcykKICAgIC0gc3RhcnQgd2l0aCA1IGhlcmUgYmVjYXVzZSB3ZSBrbm93IHRoZXJlIGFyZSA1IGFjdHMgaW4gdGhlIHBsYXkKCgpgYGB7cn0KIyBjb252ZXJ0IHRvIERvY3VtZW50IFRlcm0gTWF0cml4CgpNb2Rlcm5NYWNiZXRoX0RUTSA8LSAKICBNb2Rlcm5NYWNiZXRoICU+JQogIG11dGF0ZShhY3Rfc2NlbmUgPSBnc3ViKHBhdHRlcm4gPSAiXFwudHh0IiwgcmVwbGFjZW1lbnQgPSAiIiwgeCA9IGRvY19pZCkpICU+JQogIHVubmVzdF90b2tlbnMob3V0cHV0ID0gd29yZCwgaW5wdXQgPSB0ZXh0KSAlPiUKICBtdXRhdGUod29yZCA9IGdzdWIocGF0dGVybiA9ICLigJkiLCByZXBsYWNlbWVudCA9ICInIiwgeCA9IHdvcmQpKSAlPiUKICBhbnRpX2pvaW4oc3RvcF93b3JkcywgYnkgPSBjKCJ3b3JkIiA9ICJ3b3JkIikpICU+JSAgICAgCiAgY291bnQoYWN0X3NjZW5lLCB3b3JkLCBzb3J0ID0gVFJVRSkgJT4lICAgCiAgY2FzdF9kdG0oZG9jdW1lbnQgPSBhY3Rfc2NlbmUsIHRlcm0gPSB3b3JkLCB2YWx1ZSA9IG4pCgoKYGBgCgpgYGB7cn0KIyBMREEgdG9waWMgbW9kZWwgKDUgdG9waWNzKQpzY2VuZXNfbGRhIDwtIAogIE1vZGVybk1hY2JldGhfRFRNICU+JQogIExEQShrID0gNSwgY29udHJvbCA9IGxpc3Qoc2VlZCA9IDM4MCkpICAKCgojIFBlci10b3BpYy1wZXItd29yZCBwcm9iYWJpbGl0aWVzIApzY2VuZV90b3BpY3MgPC0gdGlkeShzY2VuZXNfbGRhLCBtYXRyaXggPSAiYmV0YSIpCnNjZW5lX3RvcGljcwoKCmBgYAoKCmBgYHtyfQojIFZpc3VhbGl6ZSB0b3AgcGVyLXRvcGljLXBlci13b3JkIHByb2JhYmlsaXRpZXMKdG9wX3Rlcm1zIDwtIAogIHNjZW5lX3RvcGljcyAlPiUKICBncm91cF9ieSh0b3BpYykgJT4lCiAgdG9wX24oNSwgYmV0YSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UodG9waWMsIC1iZXRhKQoKdG9wX3Rlcm1zICU+JQogIG11dGF0ZSh0ZXJtID0gcmVvcmRlcih0ZXJtLCBiZXRhKSkgJT4lCiAgZ2dwbG90KGFlcyh0ZXJtLCBiZXRhLCBmaWxsID0gZmFjdG9yKHRvcGljKSkpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZmFjZXRfd3JhcCh+IHRvcGljLCBzY2FsZXMgPSAiZnJlZSIpICsKICBjb29yZF9mbGlwKCkKCgoKYGBgCgoKYGBge3J9CiMgUGVyLWRvY3VtZW50IGNsYXNzaWZpY2F0aW9uCnNjZW5lc19nYW1tYSA8LSB0aWR5KHNjZW5lc19sZGEsIG1hdHJpeCA9ICJnYW1tYSIpCnNjZW5lc19nYW1tYSAlPiUgCiAgYXJyYW5nZShkb2N1bWVudCwgZ2FtbWEpCgpgYGAKCgpgYGB7cn0KIyBUb3BpY3MgdnMgQWN0cz8Kc2NlbmVzX2dhbW1hIDwtIAogIHNjZW5lc19nYW1tYSAlPiUKICBzZXBhcmF0ZShjb2wgPSBkb2N1bWVudCwgaW50byA9IGMoImFjdCIsICJzY2VuZSIpLCBzZXAgPSAiXyIsIGNvbnZlcnQgPSBUUlVFKQoKc2NlbmVzX2dhbW1hICU+JQogIGdncGxvdChhZXMoZmFjdG9yKHRvcGljKSwgZ2FtbWEpKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIGZhY2V0X3dyYXAofiBhY3QpCgpgYGAKCgoKIyBTY2VuZSBhbGlnbm1lbnQgdG8gdG9waWNzCgotIE9rYXksIHRoZSB0b3BpY3MgYXJlbid0IHRoZSBhY3RzCi0gdHVybnMgb3V0IHRoaXMgZW5kdXJpbmcgU2hha2VzcGVhcmVhbiBtYXN0ZXJwaWVjZSBuZWVkcyBhIG1vcmUgbnVhbmNlZCBpbnRlcnByZXRhdGlvbgotIExpdGVyYXJ5IHRoZW1lcyBjb3VsZCBhbGlnbiBiZXR0ZXIgdG8gdGhlIFRvcGljcyBpZGVudGlmaWVkIGJ5IG91ciBMREEuLi4KLSBoZXJlcyBvbmUgYXR0ZW1wdCBJIGZvdW5kLCB3aXRoIG15IHdpZmUncyBkZXNjcmlwdGlvbiBvZiBlYWNoIGNvbG9yOgogICAgLSBhbWJpdGlvbiAobGF2ZW5kYXIpCiAgICAtIGZhdGUgKHBvcHB5KQogICAgLSB2aW9sZW5jZSAoVGlmZmFueSBibHVlKQogICAgLSBuYXR1cmUgJiB1bm5hdHVyYWwgKHNhbmQpCiAgICAtIG1hc2N1bGluaXR5IChLZW5kYWxsIGdyYXkpCgohW2h0dHBzOi8vd3d3LmxpdGNoYXJ0cy5jb20vbGl0L21hY2JldGgvY2hhcnQtYm9hcmQtdmlzdWFsaXphdGlvbl0oTWFjYmV0aExpdGVyYXJ5VGhlbWVzLnBuZyl7IHdpZHRoPTkwJSB9CgoKCiMgUm9hZCBtYXAgKGFnYWluKQoKPGNlbnRlcj4KCiFbaHR0cHM6Ly93d3cudGlkeXRleHRtaW5pbmcuY29tL3RvcGljbW9kZWxpbmcuaHRtbF0odGlkeXRleHRGbG93LnBuZyl7d2lkdGg9OTUlfQoKPC9jZW50ZXI+CgoKCgoK